मुद्रा का प्रतिनिधित्व करने के लिए डबल या फ्लोट का उपयोग क्यों नहीं करें?


938

मुझे हमेशा से कहा गया है कि मैं कभी भी पैसे doubleया floatप्रकारों का प्रतिनिधित्व नहीं कर सकता , और इस बार मैंने आपसे प्रश्न पूछा: क्यों?

मुझे यकीन है कि एक बहुत अच्छा कारण है, मुझे नहीं पता कि यह क्या है।


4
इस SO प्रश्न को देखें: राउंडिंग एरर्स?
जेफ ओगाटा

80
बस स्पष्ट होने के लिए, उन्हें ऐसी किसी भी चीज़ के लिए उपयोग नहीं किया जाना चाहिए जिसमें सटीकता की आवश्यकता होती है - न केवल मुद्रा।
जेफ

152
उनका उपयोग किसी भी ऐसी चीज के लिए नहीं किया जाना चाहिए जिसकी सटीकता की आवश्यकता हो । लेकिन डबल के 53 महत्वपूर्ण बिट्स (~ 16 दशमलव अंक) आमतौर पर उन चीजों के लिए पर्याप्त हैं जो केवल सटीकता की आवश्यकता होती है
dan04

21
@ जफ आपकी टिप्पणी पूरी तरह से गलत बयाँ करती है कि बाइनरी फ़्लोटिंग पॉइंट किसके लिए अच्छा है और यह किसके लिए अच्छा नहीं है। नीचे दिए गए उत्तर को पढ़ें, और कृपया अपनी भ्रामक टिप्पणी को हटा दें।
पास्कल क्यूक

जवाबों:


983

क्योंकि फ़्लोट्स और डबल्स आधार 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प्रकार है।


3
@ फ़रमान से आपको गोलमोल त्रुटियाँ मिलेंगी और कुछ मामलों में जहाँ बड़ी मात्रा में मुद्रा का उपयोग किया जा रहा है, ब्याज दर की गणना
स्थाई रूप से

5
... सबसे आधार 10 अंश, यानी। उदाहरण के लिए, 0.1 में कोई सटीक बाइनरी फ्लोटिंग-पॉइंट प्रतिनिधित्व नहीं है। तो, 1.0 / 10 * 101.0 के समान नहीं हो सकता है।
क्रिस जस्टर-यंग

6
@ linuxuser27 मुझे लगता है कि फ्रेंक मजाकिया बनने की कोशिश कर रहा था। वैसे भी, ज़्नक का जवाब सबसे अच्छा मैंने देखा है, बलोच द्वारा क्लासिक संस्करण से भी बेहतर है।
आइजैक राबिनोविच

5
बेशक यदि आप सटीक जानते हैं, तो आप हमेशा परिणाम को गोल कर सकते हैं और इस तरह पूरे मुद्दे से बच सकते हैं। यह BigDecimal का उपयोग करने की तुलना में बहुत तेज और सरल है। एक अन्य विकल्प निश्चित सटीक इंट या लंबे समय तक उपयोग करना है।
पीटर लॉरी

2
@zneak आप जानते हैं कि तर्कसंगत संख्याएं वास्तविक संख्याओं का सबसेट हैं , है ना? आईईईई-754 वास्तविक संख्या रहे हैं वास्तविक संख्या। वे सिर्फ तर्कसंगत होने के लिए भी होते हैं।
टिम सेगिन

314

बलोच, जे। से, प्रभावी जावा, दूसरा संस्करण, आइटम 48:

floatऔर doubleप्रकार के विशेष रूप से मौद्रिक गणना के लिए बीमार अनुकूल हैं क्योंकि यह असंभव है एक के रूप में 0.1 (या दस के किसी भी अन्य नकारात्मक शक्ति) का प्रतिनिधित्व करने floatया doubleबिल्कुल।

उदाहरण के लिए, मान लीजिए कि आपके पास $ 1.03 है और आप 42c खर्च करते हैं। आपके पास कितना पैसा बचा है?

System.out.println(1.03 - .42);

प्रिंट करता है 0.6100000000000001

इस समस्या को हल करने के लिए सही तरीके से उपयोग करने के लिए है BigDecimal, intया long मौद्रिक गणना के लिए।

हालांकि BigDecimalकुछ चेतावनी हैं (कृपया वर्तमान में स्वीकृत उत्तर देखें)।


6
मौद्रिक गणना के लिए int या लंबे समय तक उपयोग करने की सिफारिश से मैं थोड़ा भ्रमित हूं। आप एक इंट या लंबे समय के रूप में 1.03 का प्रतिनिधित्व कैसे करते हैं? मैंने कोशिश की है "लंबे समय तक = 1.04;" और "लॉन्ग ए = 104/100;" कोई फायदा नहीं।
पीटर

49
@ पैटर, आप long a = 104डॉलर के बजाय सेंट में उपयोग और गणना करते हैं ।
zneak

@ ज़नक जब एक प्रतिशत को चक्रवृद्धि ब्याज या इसी तरह लागू करने की आवश्यकता होती है तो क्या होगा?
त्रुक्त्र

3
@trusktr, मैं आपके प्लेटफ़ॉर्म के दशमलव प्रकार के साथ जाऊँगा। जावा में, वह BigDecimal
zneak

13
@maaartinus ... और आपको नहीं लगता कि ऐसी चीज़ों के लिए डबल का उपयोग करना त्रुटि-प्रवण है? मैंने देखा है कि फ्लोटिंग राउंडिंग मुद्दा वास्तविक सिस्टम को कड़ी टक्कर देता है । बैंकिंग में भी। कृपया इसकी अनुशंसा न करें, या यदि आप करते हैं, तो एक अलग उत्तर के रूप में प्रदान करें (ताकि हम इसे डाउनवोट कर सकें: P)
eis

74

यह सटीकता का मामला नहीं है, न ही यह सटीकता का मामला है। यह उन मनुष्यों की अपेक्षाओं को पूरा करने का मामला है जो आधार के बजाय गणना के लिए बेस 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 का उपयोग करते हैं, तो उत्तर हमेशा वही होते हैं जो मनुष्यों द्वारा अपेक्षित होते हैं, आपके कोड में कोई अन्य बग नहीं मानते हैं।


2
संबंधित, दिलचस्प: मेरे क्रोम जेएस कंसोल में: Math.round ((.4999999999999999): 0:। आसपास ((.49999999999999999): 1
कर्टिस

16
यह उत्तर भ्रामक है। 1.40 * 165 = 231. किसी भी वास्तव में 231 अतिरिक्त किसी अन्य नंबर है एक गणितीय अर्थ (और अन्य सभी इंद्रियों) में गलत।
कारू

2
@ मुझे लगता है कि इसीलिए रैंडी का कहना है कि फ्लोट्स खराब हैं ... परिणाम के रूप में मेरा Chrome JS कंसोल 230.99999999999997 दिखाता है। यही कारण है कि है जो जवाब में की गई है गलत,।
त्रुष्टक

6
@ केरू: इमो जवाब गणितीय रूप से गलत नहीं है। यह सिर्फ इतना है कि 2 प्रश्न हैं, जिनका उत्तर दिया जा रहा है, जो प्रश्न नहीं पूछा जा रहा है। प्रश्न आपके संकलक के उत्तर 1.39999999 * 164.99999999 और इसी तरह से गणितीय रूप से सही 230 230999 के बराबर है। जाहिर है कि यह सवाल पहली बार में नहीं पूछा गया था ....
मार्क

2
@CurtisYallop क्योंकि 0.49999999999999999 पर डबल मूल्य बंद हो जाता है 0.5 वापसी 1 क्यों होती है Math.round(0.49999999999999994)?
फुक्लेव

53

मैं इनमें से कुछ प्रतिक्रियाओं से परेशान हूं। मुझे लगता है कि युगल और फ़्लोट्स की वित्तीय गणना में एक जगह है। निश्चित रूप से, गैर-भिन्नात्मक मौद्रिक राशियों को जोड़ने और घटाने पर पूर्णांक कक्षाओं या बिगडेसीमल वर्गों का उपयोग करते समय परिशुद्धता का कोई नुकसान नहीं होगा। लेकिन जब अधिक जटिल ऑपरेशन करते हैं, तो आप अक्सर ऐसे परिणामों के साथ समाप्त होते हैं जो कई या कई दशमलव स्थानों पर जाते हैं, चाहे आप संख्याओं को कैसे संग्रहीत करें। मुद्दा यह है कि आप परिणाम कैसे प्रस्तुत करते हैं।

यदि आपका रिजल्ट राउंड अप और राउंड डाउन होने के बीच की सीमा रेखा पर है, और वह अंतिम पैसा वास्तव में मायने रखता है, तो आपको संभवतः दर्शकों को बताना चाहिए कि उत्तर लगभग बीच में है - अधिक दशमलव स्थानों को प्रदर्शित करके।

डबल्स के साथ समस्या, और इसी तरह फ़्लोट्स के साथ, जब वे बड़ी संख्या और छोटी संख्याओं को संयोजित करने के लिए उपयोग किए जाते हैं। जावा में,

System.out.println(1000000.0f + 1.2f - 1000000.0f);

का परिणाम

1.1875

16
इस!!!! मैं इस शानदार कारखाने को खोजने के लिए सभी उत्तरों की खोज कर रहा था !!! सामान्य गणनाओं में अगर किसी को कुछ प्रतिशत तक की परवाह नहीं है, लेकिन यहां उच्च संख्या के साथ आसानी से कुछ डॉलर प्रति लेनदेन खो जाते हैं!
फाल्को

22
, और एक वर्ष के बाद वह मिल गया नहीं 1000 डॉलर हो जाएगा ताकि मामला - और अब कल्पना किसी ने उसके 1 मिलियन डॉलर पर 0.01% की दैनिक आय हो रही है - वह प्रत्येक दिन कुछ भी नहीं मिलेगा
फाल्को

6
समस्या सटीकता नहीं है, लेकिन यह फ्लोट आपको नहीं बताता है कि यह गलत है। एक पूर्णांक केवल 10 अंकों तक पकड़ सकता है एक फ्लोट गलत होने के बिना 6 तक पकड़ सकता है (जब आप तदनुसार कटौती करते हैं)। यह पूर्णांक के ओवरफ्लो होने पर यह अनुमति देता है और जावा जैसी भाषा आपको चेतावनी देगी या इसकी अनुमति नहीं देगी। जब आप दोहरे का उपयोग करते हैं, तो आप 16 अंकों तक जा सकते हैं जो कई उपयोग मामलों के लिए पर्याप्त है।
सिगी

39

फ़्लोट्स और डबल्स अनुमानित हैं। यदि आप एक BigDecimal बनाते हैं और कंस्ट्रक्टर में एक फ्लोट पास करते हैं, तो आप देखते हैं कि फ्लोट वास्तव में बराबर है:

groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375

यह शायद नहीं है कि आप $ 1.01 का प्रतिनिधित्व कैसे करना चाहते हैं।

समस्या यह है कि IEEE युक्ति में सभी भिन्नों को ठीक से दर्शाने का कोई तरीका नहीं है, उनमें से कुछ अंत में दोहराव के रूप में होते हैं ताकि आप सन्निकटन त्रुटियों के साथ समाप्त हो जाएँ। चूँकि लेखाकार चीजों को बिलकुल पैसा देना पसंद करते हैं, और अगर ग्राहक उनके बिल का भुगतान करते हैं और भुगतान के बाद उन्हें भुगतान किया जाता है तो वे नाराज हो जाएंगे। जावा में दशमलव (C # में) या java.math.BigDecimal जैसे सटीक प्रकार।

ऐसा नहीं है कि यदि आप गोल करते हैं तो त्रुटि नियंत्रणीय नहीं है: पीटर लॉरी का यह लेख देखें । पहली जगह में गोल करना आसान नहीं है। अधिकांश एप्लिकेशन जो पैसे को संभालते हैं, बहुत सारे गणित के लिए कॉल नहीं करते हैं, संचालन में चीजों को जोड़ने या विभिन्न बाल्टियों को मात्रा आवंटित करना शामिल है। फ्लोटिंग पॉइंट और राउंडिंग का परिचय केवल चीजों को जटिल बनाता है।


5
float, doubleऔर सटीक मूल्यों का BigDecimalप्रतिनिधित्व करते हैं। कोड टू ऑब्जेक्ट रूपांतरण अक्षम हैं और साथ ही अन्य ऑपरेशन भी। प्रकार स्वयं अक्षम नहीं हैं।
chux -

1
@chux: इस पर फिर से विचार करते हुए, मुझे लगता है कि आपके पास एक बिंदु है कि मेरे शब्दों में सुधार किया जा सकता है। मैं इसे संपादित करूंगा और फिर से लिखूंगा।
नाथन ह्यूजेस

28

मुझे जोखिम में डाल दिया जाएगा, लेकिन मुझे लगता है कि मुद्रा गणना के लिए फ्लोटिंग पॉइंट नंबरों की अप्रभाव्यता खत्म हो गई है। जब तक आप यह सुनिश्चित कर लेते हैं कि आप सेंटिंग-राउंडिंग सही ढंग से करते हैं और zneak द्वारा बताए गए बाइनरी-दशमलव प्रतिनिधित्व बेमेल का मुकाबला करने के लिए पर्याप्त महत्वपूर्ण अंक हैं, तो कोई समस्या नहीं होगी।

एक्सेल में मुद्रा की गणना करने वाले लोगों ने हमेशा डबल सटीक फ़्लोट्स का उपयोग किया है (एक्सेल में कोई मुद्रा प्रकार नहीं है) और मुझे अभी तक किसी को भी राउंडिंग त्रुटियों के बारे में शिकायत करते हुए देखना है।

बेशक, आपको कारण के भीतर रहना होगा; उदाहरण के लिए, एक साधारण webshop शायद डबल सटीक फ़्लोट्स के साथ किसी भी समस्या का अनुभव नहीं करेगा, लेकिन यदि आप उदाहरण के लिए लेखांकन या कुछ और करते हैं जिसमें बड़ी (अप्रतिबंधित) संख्याओं को जोड़ने की आवश्यकता होती है, तो आप फ़्लोटिंग पॉइंट नंबरों को दस फुट से स्पर्श नहीं करना चाहेंगे पोल।


3
यह वास्तव में एक बहुत ही अच्छा जवाब है। ज्यादातर मामलों में उनका उपयोग करना पूरी तरह से ठीक है।
वाहिद अमीरी

2
यह ध्यान दिया जाना चाहिए कि अधिकांश निवेश बैंक डबल का उपयोग करते हैं जैसा कि अधिकांश सी ++ प्रोग्राम करते हैं। कुछ लंबे समय का उपयोग करते हैं, लेकिन इस प्रकार यह ट्रैकिंग स्केल की अपनी समस्या है।
पीटर लॉरी

20

हालांकि यह सच है कि फ़्लोटिंग पॉइंट प्रकार केवल अनुमानित दशमलव डेटा का प्रतिनिधित्व कर सकता है, यह भी सच है कि यदि उन्हें प्रस्तुत करने से पहले आवश्यक सटीकता के लिए एक राउंड संख्या होती है, तो एक सही परिणाम प्राप्त करता है। आम तौर पर।

आमतौर पर क्योंकि डबल प्रकार में 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;
    }
}

15

फ़्लोटिंग पॉइंट संख्या का परिणाम सटीक नहीं है, जो उन्हें किसी भी वित्तीय गणना के लिए अनुपयुक्त बनाता है जिसके लिए सटीक परिणाम की आवश्यकता होती है और अनुमानित नहीं। फ्लोट और डबल इंजीनियरिंग और वैज्ञानिक गणना के लिए डिज़ाइन किए गए हैं और कई बार सटीक परिणाम नहीं देते हैं, फ़्लोटिंग पॉइंट गणना का परिणाम जेवीएम से जेवीएम तक भिन्न हो सकता है। 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

3
आइए हम तुच्छ जोड़ / घटाव और पूर्णांक उत्परिवर्तजन के अलावा कुछ और प्रयास करें, यदि कोड ने 7% ऋण की मासिक दर की गणना की है, तो दोनों प्रकारों को एक सटीक मान प्रदान करने और निकटतम 0.01 की आवश्यकता होगी। सबसे कम मौद्रिक इकाई के लिए गोलाई धन गणनाओं का एक हिस्सा है, दशमलव प्रकारों का उपयोग करने से अतिरिक्त / घटाव के साथ की आवश्यकता से बचते हैं - लेकिन बहुत अधिक नहीं।
chux -

@ chux-ReinstateMonica: यदि ब्याज मासिक माना जाता है, तो हर महीने दैनिक शेष राशि को जोड़कर ब्याज की गणना करें, जो कि 7 (ब्याज दर) से गुणा करें, और विभाजित करें, निकटतम पेनी में, दिनों की संख्या से वर्ष। बहुत अंतिम चरण पर प्रति माह एक बार के अलावा कहीं भी गोलाई नहीं।
सुपरकैट

@supercat मेरी टिप्पणी सबसे छोटी मौद्रिक इकाई के एक बाइनरी एफपी या एक दशमलव एफपी का उपयोग करने पर जोर देती है, दोनों समान गोल मुद्दों को भड़काते हैं - जैसे आपकी टिप्पणी में "और विभाजित, निकटतम पेनी के लिए गोल"। बेस 2 या बेस 10 एफपी का उपयोग करने से आपके परिदृश्य में कोई फायदा नहीं होता है।
chux -

@ chux-ReinstateMonica: उपरोक्त परिदृश्य में, अगर गणित यह बताता है कि ब्याज कुछ संख्या में आधे-सेंट के बराबर होना चाहिए, तो एक सही वित्तीय कार्यक्रम ठीक-ठीक निर्दिष्ट फैशन में होना चाहिए। यदि फ्लोटिंग-पॉइंट गणना में $ 1.23499941 का ब्याज मूल्य निकलता है, लेकिन गोलाई से पहले गणितीय-सटीक मूल्य $ 1.235 होना चाहिए था और गोलाई को "निकटतम समीप" के रूप में निर्दिष्ट किया गया है, तो ऐसे फ्लोटिंग-पॉइंट गणनाओं का उपयोग करने का परिणाम नहीं होगा $ 0.000059 से दूर रहें, बल्कि पूरे 0.01 डॉलर से, जो लेखांकन उद्देश्यों के लिए जस्ट प्लेन गलत है।
सुपरकैट

@supercat बाइनरी doubleएफपी का उपयोग करते हुए प्रतिशत को 0.5 प्रतिशत की गणना करने में कोई परेशानी नहीं होगी और न ही दशमलव एफपी। यदि फ़्लोटिंग-पॉइंट गणनाएँ उदाहरण के लिए 123.499941 का ब्याज मान देती हैं, या तो बाइनरी एफपी या दशमलव एफपी के माध्यम से, डबल राउंडिंग समस्या समान है - कोई फायदा नहीं है। आपका आधार गणितीय-सटीक मान को लगता है और दशमलव FP समान हैं - कुछ भी दशमलव FP की गारंटी नहीं देता है। 0.5 / 7.0 * 7.0 द्विआधारी और विकर्ण एफपी के लिए एक समस्या है। IAC, सबसे अधिक मूट किया जाएगा क्योंकि मुझे उम्मीद है कि दशमलव FP प्रदान करने के लिए C का अगला संस्करण होगा।
चक्स -

11

जैसा कि पहले कहा गया था "धन को एक डबल या फ्लोट के रूप में प्रदर्शित करना संभवत: पहले अच्छा लगेगा क्योंकि सॉफ्टवेयर छोटी-मोटी त्रुटियों के चक्कर लगाता है, लेकिन जैसा कि आप अधिक संख्याओं में जोड़, घटाव, गुणा और भाग करते हैं, तो आप अधिक से अधिक सटीक खो देंगे। जैसे-जैसे त्रुटियां बढ़ती जाती हैं, यह पैसे से निपटने के लिए फ्लोट और डबल्स को अपर्याप्त बनाता है, जहां आधार 10 शक्तियों के गुणकों के लिए सही सटीकता की आवश्यकता होती है। "

अंत में जावा के पास मुद्रा और धन के साथ काम करने का एक मानक तरीका है!

JSR 354: मुद्रा और मुद्रा एपीआई

JSR 354 धन और मुद्रा के साथ व्यापक गणना का प्रतिनिधित्व, परिवहन और प्रदर्शन के लिए एक एपीआई प्रदान करता है। आप इसे इस लिंक से डाउनलोड कर सकते हैं:

JSR 354: मुद्रा और मुद्रा एपीआई डाउनलोड

विनिर्देशन में निम्नलिखित चीजें शामिल हैं:

  1. मौद्रिक राशि और मुद्राओं को संभालने के लिए एक एपीआई
  2. विनिमेय कार्यान्वयन का समर्थन करने के लिए एपीआई
  3. कार्यान्वयन वर्गों के उदाहरण बनाने के लिए फैक्टरियाँ
  4. मौद्रिक राशियों की गणना, रूपांतरण और प्रारूपण के लिए कार्यशीलता
  5. जावा एपीआई मनी और मुद्राओं के साथ काम करने के लिए, जिसे जावा 9 में शामिल करने की योजना है।
  6. सभी विनिर्देश वर्ग और इंटरफेस 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 - मुद्रा और धन


5

यदि आपकी गणना में विभिन्न चरण शामिल हैं, तो मनमाना सटीक अंकगणित आपको 100% कवर नहीं करेगा।

परिणामों का एक सही प्रतिनिधित्व का उपयोग करने का एकमात्र विश्वसनीय तरीका (एक कस्टम फ़्रैक्शन डेटा प्रकार का उपयोग करें जो अंतिम चरण में विभाजन कार्यों को बैच देगा) और केवल अंतिम चरण में दशमलव संकेतन में परिवर्तित हो जाएगा।

मनमाना परिशुद्धता मदद नहीं करेगा क्योंकि हमेशा ऐसे संख्याएं हो सकती हैं जिनमें बहुत सारे दशमलव स्थान हों, या कुछ परिणाम जैसे 0.6666666... कोई भी मनमाना प्रतिनिधित्व अंतिम उदाहरण को कवर नहीं करेगा। इसलिए आपको प्रत्येक चरण में छोटी त्रुटियां होंगी।

इन त्रुटियों को जोड़ देगा, अंततः अब और अनदेखा करना आसान नहीं होगा। इसे त्रुटि प्रसार कहा जाता है ।


4

अधिकांश जवाबों ने उन कारणों पर प्रकाश डाला है कि किसी को धन और मुद्रा गणना के लिए युगल का उपयोग क्यों नहीं करना चाहिए। और मैं उनसे पूरी तरह सहमत हूं।

हालांकि इसका मतलब यह नहीं है कि डबल्स का इस्तेमाल उस उद्देश्य के लिए कभी नहीं किया जा सकता है।

मैंने बहुत कम gc आवश्यकताओं वाली कई परियोजनाओं पर काम किया है, और उस ओवरहेड में BigDecimal वस्तुओं का बड़ा योगदान था।

यह दोहरे प्रतिनिधित्व के बारे में समझ की कमी और सटीकता और सटीकता से निपटने में अनुभव की कमी है जो इस बुद्धिमान सुझाव के बारे में लाता है।

यदि आप अपनी परियोजना की सटीकता और सटीकता की आवश्यकताओं को संभालने में सक्षम हैं, तो आप इसे काम कर सकते हैं, जो कि दोहरे मानों की एक सीमा के आधार पर किया जाना है।

आप अधिक विचार प्राप्त करने के लिए अमरूद की फजी कॉमपार्ट विधि का उल्लेख कर सकते हैं। पैरामीटर सहिष्णुता कुंजी है। हम एक सिक्योरिटीज ट्रेडिंग एप्लिकेशन के लिए इस समस्या से निपटते हैं और हमने अलग-अलग रेंज में अलग-अलग संख्यात्मक मूल्यों के लिए क्या सहिष्णुता है, इस पर एक संपूर्ण शोध किया।

इसके अलावा, ऐसी परिस्थितियां भी हो सकती हैं जब आपको डबल रैपर का उपयोग करने के लिए एक मानचित्र कुंजी के रूप में उपयोग करने के लिए लुभाया जाता है। यह बहुत जोखिम भरा है क्योंकि उदाहरण मान "0.5" और "0.6 - 0.1" के लिए Double.equals और हैश कोड एक बड़ी गड़बड़ी का कारण होगा।


2

इस सवाल पर पोस्ट किए गए जवाबों में से कई IEEE और फ्लोटिंग-पॉइंट अंकगणित के आसपास के मानकों पर चर्चा करते हैं।

एक गैर-कंप्यूटर विज्ञान पृष्ठभूमि (भौतिकी और इंजीनियरिंग) से आते हुए, मैं समस्याओं को एक अलग दृष्टिकोण से देखता हूं। मेरे लिए, गणितीय गणना में डबल या फ्लोट का उपयोग नहीं करने का कारण यह है कि मैं बहुत अधिक जानकारी खो दूंगा।

विकल्प क्या हैं? कई (और कई और भी हैं जिनके बारे में मुझे जानकारी नहीं है!)।

Java में BigDecimal जावा भाषा का मूल है। Apfloat जावा के लिए एक और मनमाना-सटीक पुस्तकालय है।

C # में दशमलव डेटा प्रकार 28 महत्वपूर्ण आंकड़ों के लिए Microsoft का .NET विकल्प है।

SciPy (साइंटिफिक पाइथन) शायद वित्तीय गणना भी संभाल सकता है (मैंने कोशिश नहीं की है, लेकिन मुझे संदेह है)।

जीएनयू मल्टीपल प्रिसिजन लाइब्रेरी (जीएमपी) और जीएनयू एमएफपीआर लाइब्रेरी सी और सी ++ के लिए दो स्वतंत्र और ओपन-सोर्स संसाधन हैं।

जावास्क्रिप्ट (!) के लिए संख्यात्मक सटीक पुस्तकालय भी हैं और मुझे लगता है कि PHP वित्तीय गणना को संभाल सकती है।

मालिकाना (विशेष रूप से, मुझे लगता है, फोरट्रान के लिए) और ओपन-सोर्स समाधान के साथ-साथ कई कंप्यूटर भाषाओं के लिए भी हैं।

मैं प्रशिक्षण से कंप्यूटर वैज्ञानिक नहीं हूं। हालाँकि, मैं जावा में BigDecimal या C # में दशमलव की ओर झुकाव करता हूं। मैंने उनके द्वारा सूचीबद्ध अन्य समाधानों की कोशिश नहीं की है, लेकिन वे शायद बहुत अच्छे भी हैं।

मेरे लिए, मैं BigDecimal को पसंद करता हूं क्योंकि यह उन तरीकों का समर्थन करता है। C # का दशमलव बहुत अच्छा है, लेकिन मुझे इसके साथ काम करने का मौका नहीं मिला है, जितना मुझे चाहिए। मैं अपने खाली समय में मेरे लिए ब्याज की वैज्ञानिक गणना करता हूं, और बिगडेसिमल बहुत अच्छी तरह से काम करता है क्योंकि मैं अपने फ्लोटिंग पॉइंट नंबरों की शुद्धता निर्धारित कर सकता हूं। BigDecimal का नुकसान? यह कई बार धीमा हो सकता है, खासकर यदि आप डिवाइड विधि का उपयोग कर रहे हैं।

आप गति के लिए, C, C ++ और फोरट्रान में मुक्त और मालिकाना पुस्तकालयों को देख सकते हैं।


1
SciPy / Numpy के बारे में, फिक्स्ड-प्रिसिजन (यानी Python's दशमलव .ecimal) समर्थित नहीं है ( docs.scipy.org/doc/numpy-dev/user/basics.types.html )। कुछ फ़ंक्शन ठीक से दशमलव (उदाहरण के लिए isnan) के साथ काम नहीं करेगा। पंडों Numpy पर आधारित है और AQR, एक प्रमुख मात्रात्मक हेज-फंड में शुरू किया गया था। तो आपके पास वित्तीय गणनाओं के बारे में आपका जवाब है (किराने का हिसाब नहीं)।
कोम्टे

2

पिछले जवाबों को जोड़ने के लिए, प्रश्न में संबोधित समस्या से निपटने के दौरान, बिगडेसिमल के अलावा जावा में जोडा-मनी को लागू करने का भी विकल्प है । 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


0

कुछ उदाहरण ... यह काम करता है (वास्तव में अपेक्षा के अनुरूप काम नहीं करता है), लगभग किसी भी प्रोग्रामिंग भाषा पर ... मैंने डेल्फी, वीबीएसस्क्रिप्ट, विज़ुअल बेसिक, जावास्क्रिप्ट और अब जावा / एंड्रॉइड के साथ प्रयास किया है:

    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!!!


3
समस्या यह नहीं है कि राउंड-ऑफ त्रुटि होती है, लेकिन यह है कि आप इसके साथ सौदा नहीं करते हैं। परिणाम को दो दशमलव स्थानों पर (यदि आप सेंट चाहते हैं) और आप कर रहे हैं।
मारार्टिनस

0

फ्लोट विभिन्न डिजाइन के साथ दशमलव का द्विआधारी रूप है; वे दो अलग चीजें हैं। एक दूसरे में परिवर्तित होने पर दो प्रकारों के बीच थोड़ी त्रुटियां होती हैं। इसके अलावा, फ्लोट को वैज्ञानिक के लिए अनंत बड़ी संख्या में मूल्यों का प्रतिनिधित्व करने के लिए डिज़ाइन किया गया है। इसका मतलब है कि यह उस निश्चित संख्या के बाइट्स के साथ अत्यधिक छोटे और चरम बड़ी संख्या में खो जाने के लिए डिज़ाइन किया गया है। दशमलव, अनंत संख्या के मानों का प्रतिनिधित्व नहीं कर सकता है, यह दशमलव अंकों की संख्या को सीमित करता है। इसलिए फ्लोट और दशमलव अलग उद्देश्य के लिए हैं।

मुद्रा मूल्य के लिए त्रुटि को प्रबंधित करने के कुछ तरीके हैं:

  1. इसके बजाय सेंट में लंबे पूर्णांक और गणना का उपयोग करें।

  2. दोहरे परिशुद्धता का उपयोग करें, अपने महत्वपूर्ण अंकों को 15 तक ही रखें ताकि दशमलव बिल्कुल नकली हो सके। मूल्यों को प्रस्तुत करने से पहले दौर; गणना करते समय अक्सर गोल।

  3. जावा बिगडेसीमल जैसी दशमलव लाइब्रेरी का उपयोग करें ताकि आपको दशमलव का अनुकरण करने के लिए दोहरे का उपयोग करने की आवश्यकता न हो।

पीएस यह जानना दिलचस्प है कि हाथ में वैज्ञानिक गणना करने वाले अधिकांश ब्रांड फ्लोट के बजाय दशमलव पर काम करते हैं। इसलिए कोई भी शिकायत फ्लोट रूपांतरण त्रुटियों को नहीं करती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.