मुझे हमेशा से कहा गया है कि मैं कभी भी पैसे double
या float
प्रकारों का प्रतिनिधित्व नहीं कर सकता , और इस बार मैंने आपसे प्रश्न पूछा: क्यों?
मुझे यकीन है कि एक बहुत अच्छा कारण है, मुझे नहीं पता कि यह क्या है।
मुझे हमेशा से कहा गया है कि मैं कभी भी पैसे double
या float
प्रकारों का प्रतिनिधित्व नहीं कर सकता , और इस बार मैंने आपसे प्रश्न पूछा: क्यों?
मुझे यकीन है कि एक बहुत अच्छा कारण है, मुझे नहीं पता कि यह क्या है।
जवाबों:
क्योंकि फ़्लोट्स और डबल्स आधार 10 का प्रतिनिधित्व सही ढंग से नहीं कर सकते हैं जो हम पैसे के लिए उपयोग करते हैं। यह मुद्दा सिर्फ जावा के लिए नहीं है, यह किसी भी प्रोग्रामिंग भाषा के लिए है जो बेस 2 फ्लोटिंग-पॉइंट प्रकारों का उपयोग करता है।
आधार 10 में, आप 10.25 को 1025 * 10 -2 (पूर्णांक बार 10 की शक्ति) के रूप में लिख सकते हैं । IEEE-754 फ़्लोटिंग-पॉइंट नंबर अलग हैं, लेकिन उनके बारे में सोचने का एक बहुत ही सरल तरीका दो की शक्ति से गुणा करना है। उदाहरण के लिए, आप 164 * 2 -4 (दो बार एक पूर्णांक की शक्ति) देख सकते हैं, जो 10.25 के बराबर भी है। यह नहीं है कि स्मृति में संख्याओं का प्रतिनिधित्व कैसे किया जाता है, लेकिन गणित के निहितार्थ समान हैं।
यहां तक कि आधार 10 में, यह अंकन अधिकांश सरल अंशों का सही प्रतिनिधित्व नहीं कर सकता है। उदाहरण के लिए, आप 1/3 का प्रतिनिधित्व नहीं कर सकते: दशमलव प्रतिनिधित्व दोहरा रहा है (0.3333 ...), इसलिए कोई भी पूर्णांक नहीं है जिसे आप 1/3 प्राप्त करने के लिए 10 की शक्ति से गुणा कर सकते हैं। आप 333 के एक लंबे अनुक्रम और 333333333 * 10 -10 की तरह एक छोटे से व्यवस्थित कर सकते हैं , लेकिन यह सटीक नहीं है: यदि आप 3 से गुणा करते हैं, तो आपको 1 नहीं मिलेगा।
हालांकि, पैसे गिनने के उद्देश्य के लिए, कम से कम उन देशों के लिए जिनका पैसा अमेरिकी डॉलर के परिमाण के एक क्रम के भीतर मूल्यवान है, आमतौर पर आप सभी को 10 -2 के गुणकों को स्टोर करने में सक्षम होना चाहिए , इसलिए यह वास्तव में मायने नहीं रखता है उस 1/3 का प्रतिनिधित्व नहीं किया जा सकता है।
फ़्लोट्स और डबल्स के साथ समस्या यह है कि बड़ी संख्या में पैसे जैसी संख्या का पूर्णांक समय की शक्ति के रूप में सटीक प्रतिनिधित्व नहीं है। वास्तव में, 0 और 1 के बीच 0.01 का एकमात्र गुणन (जो काम करते समय महत्वपूर्ण हैं) पैसे के साथ क्योंकि वे पूर्णांक सेंट हैं) जिन्हें आईईईई -754 बाइनरी फ्लोटिंग-पॉइंट नंबर के रूप में दर्शाया जा सकता है, 0, 0.25, 0.5, 0.75 और 1. अन्य सभी एक छोटी राशि से बंद हैं। 0.333333 उदाहरण के अनुरूप होने पर, यदि आप फ़्लोटिंग-पॉइंट मान 0.1 के लिए लेते हैं और आप इसे 10 से गुणा करते हैं, तो आपको 1 नहीं मिलेगा।
एक के रूप में पैसे का प्रतिनिधित्व करना double
या float
शायद पहली बार में अच्छा लगेगा क्योंकि सॉफ्टवेयर छोटी-छोटी त्रुटियों को पूरा करता है, लेकिन जैसा कि आप अधिक संख्याओं में अधिक जोड़, घटाव, गुणा और भाग करते हैं, त्रुटियां कंपाउंड होंगी और आप उन मानों के साथ समाप्त हो जाएंगे, जो अविश्वसनीय रूप से हैं सही नहीं है। यह पैसे से निपटने के लिए फ्लोट और डबल्स को अपर्याप्त बनाता है, जहां आधार 10 शक्तियों के गुणकों के लिए सही सटीकता की आवश्यकता होती है।
एक समाधान जो किसी भी भाषा के बारे में बस काम करता है, इसके बजाय पूर्णांक का उपयोग करना है, और सेंट की गणना करना है। उदाहरण के लिए, 1025 $ 10.25 होगा। पैसे से निपटने के लिए कई भाषाओं में अंतर्निहित प्रकार भी हैं। अन्य लोगों में, जावा में BigDecimal
वर्ग है, और C # का decimal
प्रकार है।
1.0 / 10 * 10
1.0 के समान नहीं हो सकता है।
बलोच, जे। से, प्रभावी जावा, दूसरा संस्करण, आइटम 48:
float
औरdouble
प्रकार के विशेष रूप से मौद्रिक गणना के लिए बीमार अनुकूल हैं क्योंकि यह असंभव है एक के रूप में 0.1 (या दस के किसी भी अन्य नकारात्मक शक्ति) का प्रतिनिधित्व करनेfloat
याdouble
बिल्कुल।उदाहरण के लिए, मान लीजिए कि आपके पास $ 1.03 है और आप 42c खर्च करते हैं। आपके पास कितना पैसा बचा है?
System.out.println(1.03 - .42);
प्रिंट करता है
0.6100000000000001
।इस समस्या को हल करने के लिए सही तरीके से उपयोग करने के लिए है
BigDecimal
,int
याlong
मौद्रिक गणना के लिए।
हालांकि BigDecimal
कुछ चेतावनी हैं (कृपया वर्तमान में स्वीकृत उत्तर देखें)।
long a = 104
डॉलर के बजाय सेंट में उपयोग और गणना करते हैं ।
BigDecimal
।
यह सटीकता का मामला नहीं है, न ही यह सटीकता का मामला है। यह उन मनुष्यों की अपेक्षाओं को पूरा करने का मामला है जो आधार के बजाय गणना के लिए बेस 10 का उपयोग करते हैं। उदाहरण के लिए, वित्तीय गणनाओं के लिए युगल का उपयोग करने से गणितीय अर्थ में "गलत" होने वाले उत्तरों का उत्पादन नहीं होता है, लेकिन यह उन उत्तरों का उत्पादन कर सकते हैं जो हैं वित्तीय अर्थों में अपेक्षित नहीं है।
यहां तक कि अगर आप आउटपुट से पहले अंतिम समय पर अपने परिणामों को गोल करते हैं, तो आप अभी भी कभी-कभी युगल का उपयोग करके एक परिणाम प्राप्त कर सकते हैं जो अपेक्षाओं से मेल नहीं खाता है।
कैलकुलेटर का उपयोग करना, या हाथ से परिणाम की गणना करना, 1.40 * 165 = 231 बिल्कुल। हालाँकि, आंतरिक रूप से मेरे कंपाइलर / ऑपरेटिंग सिस्टम वातावरण पर डबल्स का उपयोग करते हुए, यह एक द्विआधारी संख्या के रूप में 230.99999 के पास संग्रहीत किया जाता है ... इसलिए यदि आप संख्या को कम करते हैं, तो आपको 231 के बजाय 230 मिलता है। आप कारण हो सकता है कि ट्रंक करने के बजाय गोलाई करना होगा। 231 का वांछित परिणाम दिया है। यह सच है, लेकिन गोलाई में हमेशा ट्रंकेशन शामिल होता है। आप जिस भी राउंडिंग तकनीक का उपयोग करते हैं, वहाँ अभी भी इस तरह की सीमाएं हैं जो उस दौर के नीचे होंगी जब आप इसे राउंड अप करने की उम्मीद करेंगे। वे काफी दुर्लभ हैं कि वे अक्सर आकस्मिक परीक्षण या अवलोकन के माध्यम से नहीं मिलेंगे। आपको उदाहरणों के लिए कुछ कोड लिखने पड़ सकते हैं जो ऐसे परिणामों को चित्रित करते हैं जो अपेक्षा के अनुरूप व्यवहार नहीं करते हैं।
मान लें कि आप निकटतम पेनी के लिए कुछ गोल करना चाहते हैं। तो आप अपना अंतिम परिणाम लेते हैं, 100 से गुणा करते हैं, 0.5 जोड़ते हैं, काटते हैं, फिर परिणाम को 100 से विभाजित करते हैं ताकि पैसे वापस मिल सकें। यदि आपके द्वारा संग्रहीत आंतरिक संख्या 3.46499999 थी। .... 3.465 के बजाय, आप 3.46 के बजाय 3.47 प्राप्त करने जा रहे हैं, जब आप नंबर को निकटतम पेनी पर राउंड करते हैं। लेकिन आपके आधार 10 की गणना ने संकेत दिया हो सकता है कि उत्तर 3.465 होना चाहिए, जो स्पष्ट रूप से 3.47 तक होना चाहिए, न कि 3.46 तक। इस प्रकार की चीजें कभी-कभी वास्तविक जीवन में होती हैं जब आप वित्तीय गणनाओं के लिए डबल्स का उपयोग करते हैं। यह दुर्लभ है, इसलिए यह अक्सर एक मुद्दे के रूप में किसी का ध्यान नहीं जाता है, लेकिन ऐसा होता है।
यदि आप डबल्स के बजाय अपनी आंतरिक गणना के लिए बेस 10 का उपयोग करते हैं, तो उत्तर हमेशा वही होते हैं जो मनुष्यों द्वारा अपेक्षित होते हैं, आपके कोड में कोई अन्य बग नहीं मानते हैं।
Math.round(0.49999999999999994)
?
मैं इनमें से कुछ प्रतिक्रियाओं से परेशान हूं। मुझे लगता है कि युगल और फ़्लोट्स की वित्तीय गणना में एक जगह है। निश्चित रूप से, गैर-भिन्नात्मक मौद्रिक राशियों को जोड़ने और घटाने पर पूर्णांक कक्षाओं या बिगडेसीमल वर्गों का उपयोग करते समय परिशुद्धता का कोई नुकसान नहीं होगा। लेकिन जब अधिक जटिल ऑपरेशन करते हैं, तो आप अक्सर ऐसे परिणामों के साथ समाप्त होते हैं जो कई या कई दशमलव स्थानों पर जाते हैं, चाहे आप संख्याओं को कैसे संग्रहीत करें। मुद्दा यह है कि आप परिणाम कैसे प्रस्तुत करते हैं।
यदि आपका रिजल्ट राउंड अप और राउंड डाउन होने के बीच की सीमा रेखा पर है, और वह अंतिम पैसा वास्तव में मायने रखता है, तो आपको संभवतः दर्शकों को बताना चाहिए कि उत्तर लगभग बीच में है - अधिक दशमलव स्थानों को प्रदर्शित करके।
डबल्स के साथ समस्या, और इसी तरह फ़्लोट्स के साथ, जब वे बड़ी संख्या और छोटी संख्याओं को संयोजित करने के लिए उपयोग किए जाते हैं। जावा में,
System.out.println(1000000.0f + 1.2f - 1000000.0f);
का परिणाम
1.1875
फ़्लोट्स और डबल्स अनुमानित हैं। यदि आप एक BigDecimal बनाते हैं और कंस्ट्रक्टर में एक फ्लोट पास करते हैं, तो आप देखते हैं कि फ्लोट वास्तव में बराबर है:
groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375
यह शायद नहीं है कि आप $ 1.01 का प्रतिनिधित्व कैसे करना चाहते हैं।
समस्या यह है कि IEEE युक्ति में सभी भिन्नों को ठीक से दर्शाने का कोई तरीका नहीं है, उनमें से कुछ अंत में दोहराव के रूप में होते हैं ताकि आप सन्निकटन त्रुटियों के साथ समाप्त हो जाएँ। चूँकि लेखाकार चीजों को बिलकुल पैसा देना पसंद करते हैं, और अगर ग्राहक उनके बिल का भुगतान करते हैं और भुगतान के बाद उन्हें भुगतान किया जाता है तो वे नाराज हो जाएंगे। जावा में दशमलव (C # में) या java.math.BigDecimal जैसे सटीक प्रकार।
ऐसा नहीं है कि यदि आप गोल करते हैं तो त्रुटि नियंत्रणीय नहीं है: पीटर लॉरी का यह लेख देखें । पहली जगह में गोल करना आसान नहीं है। अधिकांश एप्लिकेशन जो पैसे को संभालते हैं, बहुत सारे गणित के लिए कॉल नहीं करते हैं, संचालन में चीजों को जोड़ने या विभिन्न बाल्टियों को मात्रा आवंटित करना शामिल है। फ्लोटिंग पॉइंट और राउंडिंग का परिचय केवल चीजों को जटिल बनाता है।
float
, double
और सटीक मूल्यों का BigDecimal
प्रतिनिधित्व करते हैं। कोड टू ऑब्जेक्ट रूपांतरण अक्षम हैं और साथ ही अन्य ऑपरेशन भी। प्रकार स्वयं अक्षम नहीं हैं।
मुझे जोखिम में डाल दिया जाएगा, लेकिन मुझे लगता है कि मुद्रा गणना के लिए फ्लोटिंग पॉइंट नंबरों की अप्रभाव्यता खत्म हो गई है। जब तक आप यह सुनिश्चित कर लेते हैं कि आप सेंटिंग-राउंडिंग सही ढंग से करते हैं और zneak द्वारा बताए गए बाइनरी-दशमलव प्रतिनिधित्व बेमेल का मुकाबला करने के लिए पर्याप्त महत्वपूर्ण अंक हैं, तो कोई समस्या नहीं होगी।
एक्सेल में मुद्रा की गणना करने वाले लोगों ने हमेशा डबल सटीक फ़्लोट्स का उपयोग किया है (एक्सेल में कोई मुद्रा प्रकार नहीं है) और मुझे अभी तक किसी को भी राउंडिंग त्रुटियों के बारे में शिकायत करते हुए देखना है।
बेशक, आपको कारण के भीतर रहना होगा; उदाहरण के लिए, एक साधारण webshop शायद डबल सटीक फ़्लोट्स के साथ किसी भी समस्या का अनुभव नहीं करेगा, लेकिन यदि आप उदाहरण के लिए लेखांकन या कुछ और करते हैं जिसमें बड़ी (अप्रतिबंधित) संख्याओं को जोड़ने की आवश्यकता होती है, तो आप फ़्लोटिंग पॉइंट नंबरों को दस फुट से स्पर्श नहीं करना चाहेंगे पोल।
हालांकि यह सच है कि फ़्लोटिंग पॉइंट प्रकार केवल अनुमानित दशमलव डेटा का प्रतिनिधित्व कर सकता है, यह भी सच है कि यदि उन्हें प्रस्तुत करने से पहले आवश्यक सटीकता के लिए एक राउंड संख्या होती है, तो एक सही परिणाम प्राप्त करता है। आम तौर पर।
आमतौर पर क्योंकि डबल प्रकार में 16 से कम की सटीकता होती है। यदि आपको बेहतर परिशुद्धता की आवश्यकता है तो यह उपयुक्त प्रकार नहीं है। इसके अलावा सन्निकटन जमा कर सकते हैं।
यह कहा जाना चाहिए कि भले ही आप निश्चित अंक अंकगणित का उपयोग करते हैं, फिर भी आपको संख्याओं को गोल करना होगा, क्या यह इस तथ्य के लिए नहीं था कि यदि आप समय-समय पर दशमलव संख्या प्राप्त करते हैं तो BigInteger और BigDecimal त्रुटि देते हैं। तो यहाँ भी एक सन्निकटन है।
उदाहरण के लिए, COBOL, ऐतिहासिक रूप से वित्तीय गणनाओं के लिए उपयोग किया जाता है, इसमें अधिकतम 18 आंकड़े हैं। तो अक्सर एक निहित गोलाई है।
यह कहते हुए, मेरी राय में डबल अपनी 16 अंकों की सटीकता के लिए अनुपयुक्त है, जो अपर्याप्त हो सकता है, क्योंकि यह अनुमानित नहीं है।
बाद के कार्यक्रम के निम्नलिखित आउटपुट पर विचार करें। यह दर्शाता है कि राउंडिंग डबल के बाद बिगडेसिमल को सटीक 16 तक परिणाम मिलता है।
Precision 14
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.000051110111115611
Double : 56789.012345 / 1111111111 = 0.000051110111115611
Precision 15
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.0000511101111156110
Double : 56789.012345 / 1111111111 = 0.0000511101111156110
Precision 16
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.00005111011111561101
Double : 56789.012345 / 1111111111 = 0.00005111011111561101
Precision 17
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.000051110111115611011
Double : 56789.012345 / 1111111111 = 0.000051110111115611013
Precision 18
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.0000511101111156110111
Double : 56789.012345 / 1111111111 = 0.0000511101111156110125
Precision 19
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.00005111011111561101111
Double : 56789.012345 / 1111111111 = 0.00005111011111561101252
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.MathContext;
public class Exercise {
public static void main(String[] args) throws IllegalArgumentException,
SecurityException, IllegalAccessException,
InvocationTargetException, NoSuchMethodException {
String amount = "56789.012345";
String quantity = "1111111111";
int [] precisions = new int [] {14, 15, 16, 17, 18, 19};
for (int i = 0; i < precisions.length; i++) {
int precision = precisions[i];
System.out.println(String.format("Precision %d", precision));
System.out.println("------------------------------------------------------");
execute("BigDecimalNoRound", amount, quantity, precision);
execute("DoubleNoRound", amount, quantity, precision);
execute("BigDecimal", amount, quantity, precision);
execute("Double", amount, quantity, precision);
System.out.println();
}
}
private static void execute(String test, String amount, String quantity,
int precision) throws IllegalArgumentException, SecurityException,
IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Method impl = Exercise.class.getMethod("divideUsing" + test, String.class,
String.class, int.class);
String price;
try {
price = (String) impl.invoke(null, amount, quantity, precision);
} catch (InvocationTargetException e) {
price = e.getTargetException().getMessage();
}
System.out.println(String.format("%-30s: %s / %s = %s", test, amount,
quantity, price));
}
public static String divideUsingDoubleNoRound(String amount,
String quantity, int precision) {
// acceptance
double amount0 = Double.parseDouble(amount);
double quantity0 = Double.parseDouble(quantity);
//calculation
double price0 = amount0 / quantity0;
// presentation
String price = Double.toString(price0);
return price;
}
public static String divideUsingDouble(String amount, String quantity,
int precision) {
// acceptance
double amount0 = Double.parseDouble(amount);
double quantity0 = Double.parseDouble(quantity);
//calculation
double price0 = amount0 / quantity0;
// presentation
MathContext precision0 = new MathContext(precision);
String price = new BigDecimal(price0, precision0)
.toString();
return price;
}
public static String divideUsingBigDecimal(String amount, String quantity,
int precision) {
// acceptance
BigDecimal amount0 = new BigDecimal(amount);
BigDecimal quantity0 = new BigDecimal(quantity);
MathContext precision0 = new MathContext(precision);
//calculation
BigDecimal price0 = amount0.divide(quantity0, precision0);
// presentation
String price = price0.toString();
return price;
}
public static String divideUsingBigDecimalNoRound(String amount, String quantity,
int precision) {
// acceptance
BigDecimal amount0 = new BigDecimal(amount);
BigDecimal quantity0 = new BigDecimal(quantity);
//calculation
BigDecimal price0 = amount0.divide(quantity0);
// presentation
String price = price0.toString();
return price;
}
}
फ़्लोटिंग पॉइंट संख्या का परिणाम सटीक नहीं है, जो उन्हें किसी भी वित्तीय गणना के लिए अनुपयुक्त बनाता है जिसके लिए सटीक परिणाम की आवश्यकता होती है और अनुमानित नहीं। फ्लोट और डबल इंजीनियरिंग और वैज्ञानिक गणना के लिए डिज़ाइन किए गए हैं और कई बार सटीक परिणाम नहीं देते हैं, फ़्लोटिंग पॉइंट गणना का परिणाम जेवीएम से जेवीएम तक भिन्न हो सकता है। BigDecimal के उदाहरण और दोहरी आदिम के नीचे देखें जो कि पैसे के मूल्य का प्रतिनिधित्व करने के लिए उपयोग किया जाता है, इसकी काफी स्पष्ट है कि फ्लोटिंग पॉइंट गणना सटीक नहीं हो सकती है और किसी को वित्तीय गणना के लिए BigDecimal का उपयोग करना चाहिए।
// floating point calculation
final double amount1 = 2.0;
final double amount2 = 1.1;
System.out.println("difference between 2.0 and 1.1 using double is: " + (amount1 - amount2));
// Use BigDecimal for financial calculation
final BigDecimal amount3 = new BigDecimal("2.0");
final BigDecimal amount4 = new BigDecimal("1.1");
System.out.println("difference between 2.0 and 1.1 using BigDecimal is: " + (amount3.subtract(amount4)));
आउटपुट:
difference between 2.0 and 1.1 using double is: 0.8999999999999999
difference between 2.0 and 1.1 using BigDecimal is: 0.9
double
एफपी का उपयोग करते हुए प्रतिशत को 0.5 प्रतिशत की गणना करने में कोई परेशानी नहीं होगी और न ही दशमलव एफपी। यदि फ़्लोटिंग-पॉइंट गणनाएँ उदाहरण के लिए 123.499941 का ब्याज मान देती हैं, या तो बाइनरी एफपी या दशमलव एफपी के माध्यम से, डबल राउंडिंग समस्या समान है - कोई फायदा नहीं है। आपका आधार गणितीय-सटीक मान को लगता है और दशमलव FP समान हैं - कुछ भी दशमलव FP की गारंटी नहीं देता है। 0.5 / 7.0 * 7.0 द्विआधारी और विकर्ण एफपी के लिए एक समस्या है। IAC, सबसे अधिक मूट किया जाएगा क्योंकि मुझे उम्मीद है कि दशमलव FP प्रदान करने के लिए C का अगला संस्करण होगा।
जैसा कि पहले कहा गया था "धन को एक डबल या फ्लोट के रूप में प्रदर्शित करना संभवत: पहले अच्छा लगेगा क्योंकि सॉफ्टवेयर छोटी-मोटी त्रुटियों के चक्कर लगाता है, लेकिन जैसा कि आप अधिक संख्याओं में जोड़, घटाव, गुणा और भाग करते हैं, तो आप अधिक से अधिक सटीक खो देंगे। जैसे-जैसे त्रुटियां बढ़ती जाती हैं, यह पैसे से निपटने के लिए फ्लोट और डबल्स को अपर्याप्त बनाता है, जहां आधार 10 शक्तियों के गुणकों के लिए सही सटीकता की आवश्यकता होती है। "
अंत में जावा के पास मुद्रा और धन के साथ काम करने का एक मानक तरीका है!
JSR 354: मुद्रा और मुद्रा एपीआई
JSR 354 धन और मुद्रा के साथ व्यापक गणना का प्रतिनिधित्व, परिवहन और प्रदर्शन के लिए एक एपीआई प्रदान करता है। आप इसे इस लिंक से डाउनलोड कर सकते हैं:
JSR 354: मुद्रा और मुद्रा एपीआई डाउनलोड
विनिर्देशन में निम्नलिखित चीजें शामिल हैं:
- मौद्रिक राशि और मुद्राओं को संभालने के लिए एक एपीआई
- विनिमेय कार्यान्वयन का समर्थन करने के लिए एपीआई
- कार्यान्वयन वर्गों के उदाहरण बनाने के लिए फैक्टरियाँ
- मौद्रिक राशियों की गणना, रूपांतरण और प्रारूपण के लिए कार्यशीलता
- जावा एपीआई मनी और मुद्राओं के साथ काम करने के लिए, जिसे जावा 9 में शामिल करने की योजना है।
- सभी विनिर्देश वर्ग और इंटरफेस javax.money। * पैकेज में स्थित हैं।
जेएसआर 354 का नमूना उदाहरण: मुद्रा और मुद्रा एपीआई:
एक मोनोमाउंट बनाने और कंसोल पर इसे प्रिंट करने का एक उदाहरण इस तरह दिखता है ::
MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
संदर्भ कार्यान्वयन API का उपयोग करते समय, आवश्यक कोड बहुत सरल है:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
API मौद्रिक गणना के साथ गणना का भी समर्थन करता है:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));
करेंसी यूनाइट और मोनेटअमाउंट
// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);
मोनेटअमाउंट में कई तरीके हैं जो निर्दिष्ट मुद्रा, संख्यात्मक राशि, इसकी परिशुद्धता और अधिक तक पहुंचने की अनुमति देते हैं:
MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();
int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5
// NumberValue extends java.lang.Number.
// So we assign numberValue to a variable of type Number
Number number = numberValue;
मौद्रिक राउंडिंग ऑपरेटर का उपयोग करके गोल किया जा सकता है:
CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35
जब मोनेटअमाउंट के संग्रह के साथ काम करते हैं, तो फ़िल्टरिंग, सॉर्टिंग और ग्रुपिंग के लिए कुछ अच्छी उपयोगिता विधियां उपलब्ध हैं।
List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));
कस्टम मौद्रिक संचालन
// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
return Money.of(tenPercent, amount.getCurrency());
};
MonetaryAmount dollars = Money.of(12.34567, "USD");
// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567
संसाधन:
JSR 354 के साथ जावा में पैसे और मुद्राएं संभालना
जावा 9 मनी और मुद्रा एपीआई (जेएसआर 354) में खोज
इसे भी देखें: JSR 354 - मुद्रा और धन
यदि आपकी गणना में विभिन्न चरण शामिल हैं, तो मनमाना सटीक अंकगणित आपको 100% कवर नहीं करेगा।
परिणामों का एक सही प्रतिनिधित्व का उपयोग करने का एकमात्र विश्वसनीय तरीका (एक कस्टम फ़्रैक्शन डेटा प्रकार का उपयोग करें जो अंतिम चरण में विभाजन कार्यों को बैच देगा) और केवल अंतिम चरण में दशमलव संकेतन में परिवर्तित हो जाएगा।
मनमाना परिशुद्धता मदद नहीं करेगा क्योंकि हमेशा ऐसे संख्याएं हो सकती हैं जिनमें बहुत सारे दशमलव स्थान हों, या कुछ परिणाम जैसे 0.6666666
... कोई भी मनमाना प्रतिनिधित्व अंतिम उदाहरण को कवर नहीं करेगा। इसलिए आपको प्रत्येक चरण में छोटी त्रुटियां होंगी।
इन त्रुटियों को जोड़ देगा, अंततः अब और अनदेखा करना आसान नहीं होगा। इसे त्रुटि प्रसार कहा जाता है ।
अधिकांश जवाबों ने उन कारणों पर प्रकाश डाला है कि किसी को धन और मुद्रा गणना के लिए युगल का उपयोग क्यों नहीं करना चाहिए। और मैं उनसे पूरी तरह सहमत हूं।
हालांकि इसका मतलब यह नहीं है कि डबल्स का इस्तेमाल उस उद्देश्य के लिए कभी नहीं किया जा सकता है।
मैंने बहुत कम gc आवश्यकताओं वाली कई परियोजनाओं पर काम किया है, और उस ओवरहेड में BigDecimal वस्तुओं का बड़ा योगदान था।
यह दोहरे प्रतिनिधित्व के बारे में समझ की कमी और सटीकता और सटीकता से निपटने में अनुभव की कमी है जो इस बुद्धिमान सुझाव के बारे में लाता है।
यदि आप अपनी परियोजना की सटीकता और सटीकता की आवश्यकताओं को संभालने में सक्षम हैं, तो आप इसे काम कर सकते हैं, जो कि दोहरे मानों की एक सीमा के आधार पर किया जाना है।
आप अधिक विचार प्राप्त करने के लिए अमरूद की फजी कॉमपार्ट विधि का उल्लेख कर सकते हैं। पैरामीटर सहिष्णुता कुंजी है। हम एक सिक्योरिटीज ट्रेडिंग एप्लिकेशन के लिए इस समस्या से निपटते हैं और हमने अलग-अलग रेंज में अलग-अलग संख्यात्मक मूल्यों के लिए क्या सहिष्णुता है, इस पर एक संपूर्ण शोध किया।
इसके अलावा, ऐसी परिस्थितियां भी हो सकती हैं जब आपको डबल रैपर का उपयोग करने के लिए एक मानचित्र कुंजी के रूप में उपयोग करने के लिए लुभाया जाता है। यह बहुत जोखिम भरा है क्योंकि उदाहरण मान "0.5" और "0.6 - 0.1" के लिए Double.equals और हैश कोड एक बड़ी गड़बड़ी का कारण होगा।
इस सवाल पर पोस्ट किए गए जवाबों में से कई IEEE और फ्लोटिंग-पॉइंट अंकगणित के आसपास के मानकों पर चर्चा करते हैं।
एक गैर-कंप्यूटर विज्ञान पृष्ठभूमि (भौतिकी और इंजीनियरिंग) से आते हुए, मैं समस्याओं को एक अलग दृष्टिकोण से देखता हूं। मेरे लिए, गणितीय गणना में डबल या फ्लोट का उपयोग नहीं करने का कारण यह है कि मैं बहुत अधिक जानकारी खो दूंगा।
विकल्प क्या हैं? कई (और कई और भी हैं जिनके बारे में मुझे जानकारी नहीं है!)।
Java में BigDecimal जावा भाषा का मूल है। Apfloat जावा के लिए एक और मनमाना-सटीक पुस्तकालय है।
C # में दशमलव डेटा प्रकार 28 महत्वपूर्ण आंकड़ों के लिए Microsoft का .NET विकल्प है।
SciPy (साइंटिफिक पाइथन) शायद वित्तीय गणना भी संभाल सकता है (मैंने कोशिश नहीं की है, लेकिन मुझे संदेह है)।
जीएनयू मल्टीपल प्रिसिजन लाइब्रेरी (जीएमपी) और जीएनयू एमएफपीआर लाइब्रेरी सी और सी ++ के लिए दो स्वतंत्र और ओपन-सोर्स संसाधन हैं।
जावास्क्रिप्ट (!) के लिए संख्यात्मक सटीक पुस्तकालय भी हैं और मुझे लगता है कि PHP वित्तीय गणना को संभाल सकती है।
मालिकाना (विशेष रूप से, मुझे लगता है, फोरट्रान के लिए) और ओपन-सोर्स समाधान के साथ-साथ कई कंप्यूटर भाषाओं के लिए भी हैं।
मैं प्रशिक्षण से कंप्यूटर वैज्ञानिक नहीं हूं। हालाँकि, मैं जावा में BigDecimal या C # में दशमलव की ओर झुकाव करता हूं। मैंने उनके द्वारा सूचीबद्ध अन्य समाधानों की कोशिश नहीं की है, लेकिन वे शायद बहुत अच्छे भी हैं।
मेरे लिए, मैं BigDecimal को पसंद करता हूं क्योंकि यह उन तरीकों का समर्थन करता है। C # का दशमलव बहुत अच्छा है, लेकिन मुझे इसके साथ काम करने का मौका नहीं मिला है, जितना मुझे चाहिए। मैं अपने खाली समय में मेरे लिए ब्याज की वैज्ञानिक गणना करता हूं, और बिगडेसिमल बहुत अच्छी तरह से काम करता है क्योंकि मैं अपने फ्लोटिंग पॉइंट नंबरों की शुद्धता निर्धारित कर सकता हूं। BigDecimal का नुकसान? यह कई बार धीमा हो सकता है, खासकर यदि आप डिवाइड विधि का उपयोग कर रहे हैं।
आप गति के लिए, C, C ++ और फोरट्रान में मुक्त और मालिकाना पुस्तकालयों को देख सकते हैं।
पिछले जवाबों को जोड़ने के लिए, प्रश्न में संबोधित समस्या से निपटने के दौरान, बिगडेसिमल के अलावा जावा में जोडा-मनी को लागू करने का भी विकल्प है । Java modul का नाम org.joda.money है।
इसके लिए जावा एसई 8 या उसके बाद की आवश्यकता है और इसकी कोई निर्भरता नहीं है।
अधिक सटीक होने के लिए, संकलन-समय निर्भरता है, लेकिन इसकी आवश्यकता नहीं है।
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
Joda मनी के उपयोग के उदाहरण:
// create a monetary value
Money money = Money.parse("USD 23.87");
// add another amount with safe double conversion
CurrencyUnit usd = CurrencyUnit.of("USD");
money = money.plus(Money.of(usd, 12.43d));
// subtracts an amount in dollars
money = money.minusMajor(2);
// multiplies by 3.5 with rounding
money = money.multipliedBy(3.5d, RoundingMode.DOWN);
// compare two amounts
boolean bigAmount = money.isGreaterThan(dailyWage);
// convert to GBP using a supplied rate
BigDecimal conversionRate = ...; // obtained from code outside Joda-Money
Money moneyGBP = money.convertedTo(CurrencyUnit.GBP, conversionRate, RoundingMode.HALF_UP);
// use a BigMoney for more complex calculations where scale matters
BigMoney moneyCalc = money.toBigMoney();
प्रलेखन: http://joda-money.sourceforge.net/apidocs/org/joda/money/Money.html
कार्यान्वयन के उदाहरण: https://www.programcreek.com/java-api-examples/?api=org.joda-oney
कुछ उदाहरण ... यह काम करता है (वास्तव में अपेक्षा के अनुरूप काम नहीं करता है), लगभग किसी भी प्रोग्रामिंग भाषा पर ... मैंने डेल्फी, वीबीएसस्क्रिप्ट, विज़ुअल बेसिक, जावास्क्रिप्ट और अब जावा / एंड्रॉइड के साथ प्रयास किया है:
double total = 0.0;
// do 10 adds of 10 cents
for (int i = 0; i < 10; i++) {
total += 0.1; // adds 10 cents
}
Log.d("round problems?", "current total: " + total);
// looks like total equals to 1.0, don't?
// now, do reverse
for (int i = 0; i < 10; i++) {
total -= 0.1; // removes 10 cents
}
// looks like total equals to 0.0, don't?
Log.d("round problems?", "current total: " + total);
if (total == 0.0) {
Log.d("round problems?", "is total equal to ZERO? YES, of course!!");
} else {
Log.d("round problems?", "is total equal to ZERO? NO... thats why you should not use Double for some math!!!");
}
उत्पादन:
round problems?: current total: 0.9999999999999999
round problems?: current total: 2.7755575615628914E-17
round problems?: is total equal to ZERO? NO... thats why you should not use Double for some math!!!
फ्लोट विभिन्न डिजाइन के साथ दशमलव का द्विआधारी रूप है; वे दो अलग चीजें हैं। एक दूसरे में परिवर्तित होने पर दो प्रकारों के बीच थोड़ी त्रुटियां होती हैं। इसके अलावा, फ्लोट को वैज्ञानिक के लिए अनंत बड़ी संख्या में मूल्यों का प्रतिनिधित्व करने के लिए डिज़ाइन किया गया है। इसका मतलब है कि यह उस निश्चित संख्या के बाइट्स के साथ अत्यधिक छोटे और चरम बड़ी संख्या में खो जाने के लिए डिज़ाइन किया गया है। दशमलव, अनंत संख्या के मानों का प्रतिनिधित्व नहीं कर सकता है, यह दशमलव अंकों की संख्या को सीमित करता है। इसलिए फ्लोट और दशमलव अलग उद्देश्य के लिए हैं।
मुद्रा मूल्य के लिए त्रुटि को प्रबंधित करने के कुछ तरीके हैं:
इसके बजाय सेंट में लंबे पूर्णांक और गणना का उपयोग करें।
दोहरे परिशुद्धता का उपयोग करें, अपने महत्वपूर्ण अंकों को 15 तक ही रखें ताकि दशमलव बिल्कुल नकली हो सके। मूल्यों को प्रस्तुत करने से पहले दौर; गणना करते समय अक्सर गोल।
जावा बिगडेसीमल जैसी दशमलव लाइब्रेरी का उपयोग करें ताकि आपको दशमलव का अनुकरण करने के लिए दोहरे का उपयोग करने की आवश्यकता न हो।
पीएस यह जानना दिलचस्प है कि हाथ में वैज्ञानिक गणना करने वाले अधिकांश ब्रांड फ्लोट के बजाय दशमलव पर काम करते हैं। इसलिए कोई भी शिकायत फ्लोट रूपांतरण त्रुटियों को नहीं करती है।