क्या फ़्लोटिंग पॉइंट गणित टूट गया है?


2979

निम्नलिखित कोड पर विचार करें:

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

ये अशुद्धि क्यों होती हैं?


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

62
जावास्क्रिप्ट डेसीमल को फ्लोटिंग पॉइंट नंबरों के रूप में मानता है , जिसका मतलब है कि इसके अलावा संचालन गोल त्रुटि के अधीन हो सकता है। आप इस लेख पर एक नज़र डालना चाहते हैं: फ्लोटिंग-पॉइंट अंकगणित के बारे में हर कंप्यूटर वैज्ञानिक को क्या जानना चाहिए
मैट बी

4
बस जानकारी के लिए, जावास्क्रिप्ट में सभी संख्यात्मक प्रकार IEEE-754 डबल्स हैं।
गैरी विल्बोबी

6
क्योंकि जावास्क्रिप्ट मैथ के लिए IEEE 754 मानक का उपयोग करता है, यह 64-बिट फ्लोटिंग नंबरों का उपयोग करता है । यह फ़्लोटिंग पॉइंट (दशमलव) गणना करते समय सटीक त्रुटियों का कारण बनता है, संक्षेप में, बेस 2 में काम करने वाले कंप्यूटरों के कारण, जबकि दशमलव बेस 10 है
परदीप जैन

जवाबों:


2247

बाइनरी फ्लोटिंग पॉइंट मैथ इस तरह है। अधिकांश प्रोग्रामिंग भाषाओं में, यह IEEE 754 मानक पर आधारित है । समस्या की जड़ यह है कि संख्याओं को इस प्रारूप में दो की शक्ति के रूप में दर्शाया जाता है; परिमेय संख्या (जैसे कि 0.1, जो है 1/10) जिसका भाजक दो की शक्ति नहीं है, बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है।

के लिए 0.1मानक में binary64प्रारूप, प्रतिनिधित्व बिल्कुल के रूप में लिखा जा सकता है

  • 0.1000000000000000055511151231257827021181583404541015625 दशमलव में, या
  • 0x1.999999999999ap-4में C99 hexfloat अंकन

इसके विपरीत, परिमेय संख्या 0.1, जो है 1/10, ठीक उसी प्रकार लिखी जा सकती है

  • 0.1 दशमलव में, या
  • 0x1.99999999999999...p-4C99 हेक्सफ़्लोट संकेतन के एक एनालॉग में, जहां ...9 के एक असमान अनुक्रम का प्रतिनिधित्व करता है।

स्थिरांक 0.2और 0.3आपके कार्यक्रम में भी उनके वास्तविक मूल्यों का अनुमान लगाया जाएगा। यह तब होता है सबसे करीब है कि doubleकरने के लिए 0.2तर्कसंगत संख्या से भी बड़ा है 0.2, लेकिन वह सबसे करीब doubleके लिए 0.3तर्कसंगत संख्या से कम है 0.3। की राशि 0.1और 0.2तर्कसंगत संख्या से अधिक होने के हवाएँ 0.3और इसलिए अपने कोड में निरंतर साथ असहमति।

फ्लोटिंग-पॉइंट अंकगणितीय मुद्दों का एक काफी व्यापक उपचार फ्लोटिंग-पॉइंट अंकगणित के बारे में हर कंप्यूटर वैज्ञानिक को पता होना चाहिए । एक आसान करने के लिए पचाने स्पष्टीकरण के लिए, देखें floating-point-gui.de

साइड नोट: सभी पोजिशनल (बेस-एन) नंबर सिस्टम इस समस्या को सटीक रूप से साझा करते हैं

सादे पुराने दशमलव (आधार 10) संख्याओं में एक ही समस्या है, यही वजह है कि 1/3 जैसी संख्याएँ 0.333333333 के रूप में समाप्त होती हैं ...

आपने केवल एक संख्या (3/10) पर ठोकर खाई है जो दशमलव प्रणाली के साथ प्रतिनिधित्व करना आसान होता है, लेकिन द्विआधारी प्रणाली में फिट नहीं होता है। यह दोनों तरीके (कुछ छोटी डिग्री तक) भी जाता है: 1/16 दशमलव (0.0625) में एक बदसूरत संख्या है, लेकिन बाइनरी में यह उतना ही साफ-सुथरा दिखता है जितना कि दशमलव में एक हजारवां भाग (0.0001) ** - अगर हम में था हमारे दैनिक जीवन में आधार -2 नंबर प्रणाली का उपयोग करने की आदत, आप उस संख्या को भी देखेंगे और सहज रूप से समझ पाएंगे कि आप किसी चीज को पाकर, उसे फिर से और फिर से और अधिक समय तक रोक सकते हैं।

** बेशक, यह बिल्कुल नहीं है कि फ़्लोटिंग-पॉइंट नंबर मेमोरी में कैसे संग्रहीत किए जाते हैं (वे वैज्ञानिक संकेतन के एक रूप का उपयोग करते हैं)। हालाँकि, यह इस बिंदु को स्पष्ट करता है कि बाइनरी फ़्लोटिंग-पॉइंट प्रिसिजन एरर क्रॉप करते हैं क्योंकि "रियल वर्ल्ड" नंबर जिन्हें हम आमतौर पर काम करने में रुचि रखते हैं, वे अक्सर दस की शक्तियां हैं - लेकिन केवल इसलिए कि हम एक दशमलव संख्या प्रणाली दिन का उपयोग करते हैं- आज। ऐसा इसलिए भी है क्योंकि हम "प्रत्येक 7 में से 5" के बजाय 71% जैसी चीजें कहेंगे (71% एक अनुमान है, क्योंकि 5/7 को किसी भी दशमलव संख्या के साथ बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है)।

तो नहीं: बाइनरी फ़्लोटिंग पॉइंट नंबर टूटे नहीं हैं, वे बस हर दूसरे आधार-एन नंबर सिस्टम की तरह अपूर्ण होते हैं :)

साइड साइड नोट: प्रोग्रामिंग में फ्लोट्स के साथ काम करना

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

आपको समानता परीक्षणों की तुलना करने की भी आवश्यकता है जो कुछ मात्रा में सहिष्णुता की अनुमति देते हैं, जिसका अर्थ है:

करो नहीं करनाif (x == y) { ... }

इसके बजाय करो if (abs(x - y) < myToleranceValue) { ... }

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


181
मुझे लगता है कि "कुछ त्रुटि स्थिरांक" "एप्सिलॉन" की तुलना में अधिक सही है, क्योंकि कोई "द एप्सिलॉन" नहीं है जो सभी मामलों में उपयोग किया जा सकता है। अलग-अलग स्थितियों में अलग-अलग एप्सिलॉन का उपयोग करने की आवश्यकता होती है। और मशीन एप्सिलॉन लगभग कभी भी उपयोग करने के लिए एक अच्छा स्थिरांक नहीं है।
रॉटस्टर

34
यह बिल्कुल सच नहीं है कि सभी फ्लोटिंग-पॉइंट गणित IEEE [754] मानक पर आधारित है। उदाहरण के लिए, पुराने आईबीएम हेक्साडेसिमल एफपी के उपयोग में अभी भी कुछ सिस्टम हैं, और अभी भी ग्राफिक्स कार्ड हैं जो IEEE-754 अंकगणित का समर्थन नहीं करते हैं। हालांकि, यह काफी हद तक अनुमानित है।
स्टीफन कैनन

19
गति के लिए क्रे ने IEEE-754 का अनुपालन किया। जावा ने इसके पालन को अनुकूलन के रूप में भी चुना।
आर्ट टेलर

28
मुझे लगता है कि आपको इस जवाब में कुछ जोड़ना चाहिए कि पैसे की गणना हमेशा कैसे होनी चाहिए, हमेशा पूर्णांक पर निश्चित-बिंदु अंकगणित के साथ किया जाना चाहिए , क्योंकि धन की मात्रा निर्धारित है। (यह एक प्रतिशत के छोटे अंशों में आंतरिक लेखांकन संगणना करने के लिए समझ में आ सकता है, या आपकी सबसे छोटी मुद्रा इकाई जो भी है - यह अक्सर उदाहरण के लिए "$ 29.99 प्रति माह" दैनिक दर में परिवर्तित करते समय गोल-गोल त्रुटि को कम करने में मदद करता है - लेकिन यह चाहिए अभी भी निश्चित-बिंदु अंकगणित होना चाहिए।)
zwol

18
दिलचस्प तथ्य: बाइनरी फ्लोटिंग-पॉइंट में इस 0.1 का बिल्कुल प्रतिनिधित्व नहीं किया गया, जिससे कुख्यात पैट्रियट मिसाइल सॉफ्टवेयर बग बना, जिसके परिणामस्वरूप पहले इराक युद्ध के दौरान 28 लोग मारे गए।
एचडीएल

602

एक हार्डवेयर डिज़ाइनर का परिप्रेक्ष्य

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

1 अवलोकन

इंजीनियरिंग के दृष्टिकोण से, अधिकांश फ्लोटिंग पॉइंट ऑपरेशंस में कुछ तत्व की त्रुटि होगी क्योंकि हार्डवेयर जो फ्लोटिंग पॉइंट कंप्यूटेशन करता है, उसे केवल अंतिम स्थान पर एक यूनिट के आधे से कम की त्रुटि की आवश्यकता होती है। इसलिए, बहुत से हार्डवेयर सटीक रूप से बंद हो जाएंगे, जो कि एक ही ऑपरेशन के लिए अंतिम स्थान पर एक इकाई के आधे से भी कम की त्रुटि उत्पन्न करने के लिए आवश्यक है जो कि फ्लोटिंग पॉइंट डिवीजन में विशेष रूप से समस्याग्रस्त है। एक एकल ऑपरेशन का गठन इस बात पर निर्भर करता है कि यूनिट कितने ऑपरेंड लेता है। अधिकांश के लिए, यह दो है, लेकिन कुछ इकाइयां 3 या अधिक ऑपरेंड लेती हैं। इस वजह से, इस बात की कोई गारंटी नहीं है कि बार-बार किए गए ऑपरेशन के परिणामस्वरूप एक वांछनीय त्रुटि होगी क्योंकि त्रुटियों को समय के साथ जोड़ा जाता है।

2. मानक

अधिकांश प्रोसेसर IEEE-754 मानक का पालन करते हैं , लेकिन कुछ का उपयोग असामान्य, या विभिन्न मानकों पर किया जाता है। उदाहरण के लिए, IEEE-754 में एक अपभ्रंश मोड है जो परिशुद्धता की कीमत पर बहुत छोटे फ्लोटिंग पॉइंट नंबरों के प्रतिनिधित्व की अनुमति देता है। हालाँकि, निम्नलिखित IEEE-754 के सामान्यीकृत मोड को कवर करेगा, जो ऑपरेशन का विशिष्ट मोड है।

IEEE-754 मानक में, हार्डवेयर डिजाइनरों को त्रुटि / एप्सिलॉन के किसी भी मूल्य की अनुमति दी जाती है, जब तक कि यह अंतिम स्थान में एक इकाई के आधे से कम हो, और परिणाम अंतिम में एक इकाई के आधे से भी कम हो। एक ऑपरेशन के लिए जगह। यह बताता है कि जब दोहराए जाने वाले संचालन क्यों होते हैं, तो त्रुटियां बढ़ जाती हैं। IEEE-754 दोहरी सटीकता के लिए, यह 54 वीं बिट है, क्योंकि 53 बिट्स का उपयोग संख्यात्मक भाग (सामान्यीकृत) का प्रतिनिधित्व करने के लिए किया जाता है, जिसे फ्लोटिंग पॉइंट संख्या (जैसे 5.3e5 में 5.3) के मंटिसा भी कहा जाता है। अगले अनुभाग विभिन्न फ़्लोटिंग पॉइंट ऑपरेशन पर हार्डवेयर त्रुटि के कारणों पर अधिक विस्तार से जाते हैं।

3. डिवीजन में राउंडिंग एरर का कारण

फ्लोटिंग पॉइंट डिवीज़न में त्रुटि का मुख्य कारण भाग एल्गोरिथ्म है जिसका उपयोग भागफल की गणना के लिए किया जाता है। अधिकांश कंप्यूटर सिस्टम एक व्युत्क्रम द्वारा गुणा का उपयोग करके विभाजन की गणना करते हैं, मुख्य रूप से Z=X/Y,Z = X * (1/Y)। एक विभाजन की गणना पुनरावृत्त रूप से की जाती है अर्थात प्रत्येक चक्र भागफल के कुछ बिट्स की गणना करता है जब तक कि वांछित सटीकता नहीं हो जाती है, जो IEEE-754 के लिए अंतिम स्थान में एक इकाई से कम की त्रुटि के साथ कुछ भी है। Y (1 / Y) के पारस्परिक की तालिका को धीमी श्रेणी में भागफल चयन तालिका (QST) के रूप में जाना जाता है, और भागफल चयन तालिका के बिट्स में आकार आमतौर पर मूलांक की चौड़ाई, या चयन के कई बिट्स हैं प्रत्येक पुनरावृत्ति में गणना किए गए भागफल, और कुछ गार्ड बिट्स। IEEE-754 मानक, दोहरी परिशुद्धता (64-बिट) के लिए, यह विभक्त के मूलांक के आकार, प्लस कुछ गार्ड बिट्स k, जहां होगा k>=2। उदाहरण के लिए, एक विभक्त के लिए एक विशिष्ट कोटिएंट सेलेक्शन टेबल जो एक समय में 2 भाग के भाग की गणना करता है (मूलांक 4) 2+2= 4बिट्स (प्लस कुछ वैकल्पिक बिट्स) होगा।

3.1 डिवीजन राउंडिंग त्रुटि: प्राप्तकर्ता का अनुमोदन

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

4. अन्य कार्यों में राउंडिंग एरर्स: ट्रंकेशन

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

5. दोहराया संचालन

चूँकि हार्डवेयर जो फ्लोटिंग पॉइंट गणना करता है, उसे केवल एक ही ऑपरेशन के लिए अंतिम स्थान पर एक यूनिट के आधे से भी कम की त्रुटि के साथ परिणाम प्राप्त करने की आवश्यकता होती है, यदि नहीं देखा गया तो त्रुटि बार-बार होने वाले ऑपरेशन से अधिक हो जाएगी। यही कारण है कि कम्प्यूटेशंस में, एक बाध्य त्रुटि की आवश्यकता होती है, गणितज्ञ तरीकों का उपयोग करते हैं जैसे कि IEEE-754 के अंतिम स्थान पर गोल-से-निकटतम अंकों का उपयोग करना , क्योंकि, समय के साथ, त्रुटियों को एक दूसरे को रद्द करने की अधिक संभावना है बाहर, और अंतराल अंकगणित IEEE 754 गोलाई मोड की विविधताओं के साथ संयुक्तगोलाई की त्रुटियों की भविष्यवाणी करना और उन्हें ठीक करना। अन्य राउंडिंग मोड्स की तुलना में इसकी कम सापेक्ष त्रुटि के कारण, राउंड निकटतम निकटतम अंक (अंतिम स्थान पर), IEEE-754 का डिफ़ॉल्ट राउंडिंग मोड है।

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

6. सारांश

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


8
(३) गलत है। किसी डिवीजन में गोलाई की त्रुटि अंतिम स्थान में एक इकाई से कम नहीं है, लेकिन अंतिम स्थान पर अधिकतम आधी इकाई है।
gnasher729

6
@ gnasher729 अच्छी पकड़। अधिकांश बुनियादी परिचालनों में भी डिफ़ॉल्ट IEEE राउंडिंग मोड का उपयोग करके अंतिम स्थान पर एक इकाई के 1/2 से कम की त्रुटि है। स्पष्टीकरण का संपादन किया, और यह भी नोट किया कि त्रुटि एक ulp के 1/2 से अधिक हो सकती है लेकिन 1 ulp से कम है यदि उपयोगकर्ता डिफ़ॉल्ट राउंडिंग मोड को ओवरराइड करता है (यह एम्बेडेड सिस्टम में विशेष रूप से सच है)।
कर्नेलपनिक

39
(1) फ्लोटिंग पॉइंट नंबरों में त्रुटि नहीं है। हर फ्लोटिंग पॉइंट वैल्यू वास्तव में यही है। अधिकांश (लेकिन सभी नहीं) फ़्लोटिंग पॉइंट ऑपरेशंस सटीक परिणाम देते हैं। उदाहरण के लिए, कोई बाइनरी फ्लोटिंग पॉइंट वैल्यू नहीं है जो कि 1.0 / 10.0 के बराबर है। कुछ आपरेशन (जैसे, 1.0 + 1.0) है तो दूसरी ओर सटीक परिणाम देती है।
सोलोमन स्लो

19
"फ़्लोटिंग पॉइंट डिवीज़न में त्रुटि का मुख्य कारण, भाग की गणना करने के लिए उपयोग किए जाने वाले विभाजन एल्गोरिदम हैं" कहने के लिए बहुत भ्रामक बात है। IEEE-754 के अनुरूप विभाजन के लिए, फ़्लोटिंग-पॉइंट डिवीज़न में त्रुटि का एकमात्र कारण परिणाम स्वरूप में बिल्कुल प्रतिनिधित्व किए जाने की अक्षमता है; एक ही परिणाम का उपयोग एल्गोरिथ्म की परवाह किए बिना किया जाता है।
स्टीफन कैनन

6
@ मैट देर से प्रतिक्रिया के लिए क्षमा करें। यह मूल रूप से संसाधन / समय के मुद्दों और व्यापार के कारण है। लंबे विभाजन / अधिक 'सामान्य' विभाजन को करने का एक तरीका है, इसे एसआरटी डिवीजन कहा जाता है जिसमें मूलांक दो होता है। हालाँकि, यह बार-बार विभक्त को लाभांश से अलग करता है और घटाता है और कई घड़ी चक्र लेता है क्योंकि यह केवल प्रति घड़ी चक्र के भागफल के एक बिट की गणना करता है। हम पारस्परिक की तालिकाओं का उपयोग करते हैं ताकि हम प्रति चक्र भाग के अधिक बिट्स की गणना कर सकें और प्रभावी प्रदर्शन / गति व्यापार कर सकें।
कर्नेलपनिक

462

जब आप .1 या 1/10 को बेस 2 (बाइनरी) में परिवर्तित करते हैं, तो आपको दशमलव बिंदु के बाद एक दोहराव वाला पैटर्न मिलता है, ठीक उसी तरह जैसे आधार 10 में 1/3 का प्रतिनिधित्व करने की कोशिश करना। मूल्य सटीक नहीं है, और इसलिए आप ऐसा नहीं कर सकते सामान्य फ्लोटिंग पॉइंट विधियों का उपयोग करके इसके साथ सटीक गणित।


133
शानदार और संक्षिप्त उत्तर। दोहराने का पैटर्न 0.00011001100110011001100110011001100110011001100110011001111 जैसा दिखता है ...
कॉन्स्टेंटिन

4
यह इस बात की व्याख्या नहीं करता है कि बेहतर एल्गोरिथ्म का उपयोग क्यों नहीं किया जाता है जो पहली जगह में बायनेरिज़ में परिवर्तित नहीं होता है।
दिमित्री ज़ैतसेव

12
क्योंकि प्रदर्शन। बाइनरी का उपयोग करना कुछ हजार गुना तेज है, क्योंकि यह मशीन के लिए मूल है।
जोएल कोएहॉर्न

7
ऐसे तरीके हैं जो सटीक दशमलव मान प्राप्त करते हैं BCD (बाइनरी कोडेड दशमलव) या दशमलव संख्या के विभिन्न अन्य रूप। हालांकि, ये दोनों धीमे (बहुत धीमी) हैं और बाइनरी फ्लोटिंग पॉइंट का उपयोग करने से अधिक स्टोरेज लेते हैं। (एक उदाहरण के रूप में, बीसीडी भंडार एक बाइट में 2 दशमलव अंकों के पैक यही कारण है कि एक बाइट है कि वास्तव में एक बाइट के संभावित मूल्य के 60% के बारे में 256 संभावित मान, या 100/256, जो कचरे स्टोर कर सकते हैं में 100 संभावित मान है।।)
डंकन सी

16
@Jacksonkr आप अभी भी बेस -10 में सोच रहे हैं। कंप्यूटर बेस -2 हैं।
जोएल कोएहॉर्न

306

यहाँ अधिकांश उत्तर इस प्रश्न को बहुत शुष्क, तकनीकी शब्दों में संबोधित करते हैं। मैं इसे इस संदर्भ में संबोधित करना चाहूंगा कि सामान्य मनुष्य समझ सकता है।

कल्पना करें कि आप पिज्जा को टुकड़ा करने की कोशिश कर रहे हैं। आपके पास एक रोबोट पिज्जा कटर है जो पिज्जा स्लाइस को बिल्कुल आधे में काट सकता है । यह एक पूरे पिज्जा को आधा कर सकता है, या यह एक मौजूदा स्लाइस को आधा कर सकता है, लेकिन किसी भी मामले में, हॉल्टिंग हमेशा सटीक होती है।

उस पिज्जा कटर में बहुत महीन हलचल होती है, और यदि आप पूरे पिज्जा के साथ शुरू करते हैं, तो उसे आधा कर दें, और हर बार सबसे छोटे स्लाइस को रोकते रहें, आप स्लाइस को 53 बार कर सकते हैं इससे पहले कि स्लाइस अपनी उच्च परिशुद्धता क्षमताओं के लिए बहुत छोटा हो । उस बिंदु पर, आप अब उस बहुत पतले स्लाइस को आधा नहीं कर सकते हैं, लेकिन इसे या तो शामिल करना चाहिए या इसे बाहर करना चाहिए।

अब, आप सभी स्लाइस को इस तरह से कैसे पीसेंगे जो एक पिज्जा के दसवें (0.1) या एक-पांचवें (0.2) तक जोड़ देगा? वास्तव में इसके बारे में सोचें, और इसे काम करने की कोशिश करें। तुम भी एक असली पिज्जा का उपयोग करने की कोशिश कर सकते हैं, अगर आपके पास हाथ में एक पौराणिक परिशुद्धता पिज्जा कटर है। :-)


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

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

(उन दो नंबरों के बीच का अंतर "सबसे छोटा टुकड़ा" है, जिसे हमें या तो शामिल करने का निर्णय लेना चाहिए, जो एक ऊपर के पूर्वाग्रह का परिचय देता है, या बाहर रखा जाता है, जो नीचे की ओर एक पूर्वाग्रह का परिचय देता है। उस सबसे छोटे स्लाइस के लिए तकनीकी शब्द एक ulp है ।)

0.2 के मामले में, संख्याएं सभी समान हैं, बस 2 के एक कारक द्वारा बढ़ाया जाता है। फिर, हम उस मूल्य का पक्ष लेते हैं जो 0.2 से थोड़ा अधिक है।

ध्यान दें कि दोनों मामलों में, 0.1 और 0.2 के लिए अनुमानों में थोड़ा ऊपर की ओर पूर्वाग्रह है। यदि हम इनमें से पर्याप्त गैसों को जोड़ते हैं, तो वे संख्या को आगे और आगे बढ़ाएंगे जो हम चाहते हैं, और वास्तव में, 0.1 + 0.2 के मामले में, पूर्वाग्रह इतना अधिक है कि परिणामी संख्या अब निकटतम संख्या नहीं है से 0.3।

विशेष रूप से, 0.1 + 0.2 वास्तव में 0.100000000000000005551115123125782702118158340454101562525 + 0.2000000000000000111022246251562362361668061668090,203125 = 0.30000000000000004444926006266166166166676/6326/6326/6326/6326/6326/6226/6226236 है।


PS कुछ प्रोग्रामिंग भाषाएं पिज्जा कटर भी प्रदान करती हैं जो स्लाइस को सटीक दसियों में विभाजित कर सकती हैं । हालांकि ऐसे पिज्जा कटर असामान्य हैं, यदि आपके पास एक तक पहुंच है, तो आपको इसका उपयोग तब करना चाहिए जब यह महत्वपूर्ण हो कि एक-दसवां या एक-पांचवां टुकड़ा प्राप्त करने में सक्षम हो।

(मूल रूप से Quora पर पोस्ट किया गया है।)


3
ध्यान दें कि कुछ भाषाएँ हैं जिनमें सटीक गणित शामिल है। एक उदाहरण योजना है, उदाहरण के लिए जीएनयू गुइले के माध्यम से। देखें draketo.de/english/exact-math-to-the-rescue - ये गणित को भिन्नों के रूप में रखते हैं और अंत में केवल टुकड़ा करते हैं।
अरने बेबेनहॉर्शीड

5
@FloatingRock दरअसल, बहुत कम मुख्यधारा की प्रोग्रामिंग भाषाओं में तर्कसंगत संख्याएँ अंतर्निहित हैं। Arne एक Schemer है, जैसा कि मैं हूं, इसलिए ये ऐसी चीजें हैं जिन पर हम बिगड़ जाते हैं।
क्रिस जस्टर-यंग

5
@ArneBabenhauserheide मुझे लगता है कि यह जोड़ने योग्य है कि यह केवल तर्कसंगत संख्याओं के साथ काम करेगा। इसलिए यदि आप पीआई जैसे तर्कहीन संख्याओं के साथ कुछ गणित कर रहे हैं, तो आपको इसे कई पीआई के रूप में संग्रहीत करना होगा। बेशक, पाई को शामिल करने वाली किसी भी गणना को एक सटीक दशमलव संख्या के रूप में प्रस्तुत नहीं किया जा सकता है।
ऐड़ीकापी

13
@connexo ठीक है। 36 डिग्री पाने के लिए आप अपने पिज्जा रोटेटर को कैसे प्रोग्राम करेंगे? 36 डिग्री क्या है? (संकेत: यदि आप इसे सटीक तरीके से परिभाषित करने में सक्षम हैं, तो आपके पास एक स्लाइस-ए-सटीक-तम्बू पिज्जा कटर भी है।) दूसरे शब्दों में, आपके पास वास्तव में 1/360 (एक डिग्री) या 1 / नहीं हो सकता है। केवल बाइनरी फ्लोटिंग पॉइंट के साथ 10 (36 डिग्री)।
क्रिस जस्टर-यंग

12
@connexo इसके अलावा, "हर बेवकूफ" एक पिज्जा बारी बारी से नहीं कर सकते हैं वास्तव में 36 डिग्री कम है। मनुष्य बहुत अधिक सटीक कुछ भी करने के लिए बहुत अधिक त्रुटि वाले हैं।
क्रिस जस्टर-यंग

212

फ्लोटिंग पॉइंट राउंडिंग एरर। 0.1 को बेस -2 में सही-सही दर्शाया नहीं जा सकता है क्योंकि बेस -10 में 5 का मुख्य कारक है। ठीक 1/3 के रूप में दशमलव में प्रतिनिधित्व करने के लिए अंकों की एक अनंत संख्या लगती है, लेकिन बेस -3 में "0.1" है, 0.1 आधार -2 में अनंत संख्या में अंक लेता है जहां यह आधार -10 में नहीं होता है। और कंप्यूटर में अनंत मात्रा में मेमोरी नहीं होती है।


133
कंप्यूटर को 0.1 + 0.2 = 0.3 दाहिने पाने के लिए मेमोरी की अनंत मात्रा की आवश्यकता नहीं है
Pacerier

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

15
@ स्पेसर: न तो बाइनरी और न ही दशमलव फ्लोटिंग-पॉइंट 1/3 या 1/13 स्टोर कर सकते हैं। दशमलव फ़्लोटिंग-पॉइंट प्रकार एम / 10 ^ ई के फॉर्म के मानों का सटीक रूप से प्रतिनिधित्व कर सकते हैं , लेकिन जब यह अन्य अन्य अंशों का प्रतिनिधित्व करने की बात आती है तो इसी तरह के बाइनरी फ्लोटिंग-पॉइंट संख्याओं की तुलना में कम सटीक होते हैं । कई अनुप्रयोगों में, कुछ "विशेष" लोगों के साथ पूर्ण सटीकता की तुलना में मनमाने अंशों के साथ उच्च परिशुद्धता होना अधिक उपयोगी है।
सुपरकैट

13
@ स्पेसर वे करते हैं यदि वे संख्याओं को बाइनरी फ़्लोट के रूप में संग्रहीत करते हैं, जो उत्तर का बिंदु था।
मार्क अमेरी

3
@chux: बाइनरी और दशमलव प्रकारों के बीच परिशुद्धता में अंतर बहुत बड़ा नहीं है, लेकिन दशमलव प्रकारों के लिए सबसे अच्छा-मामला बनाम सबसे खराब स्थिति में 10: 1 अंतर द्विआधारी प्रकारों के साथ 2: 1 अंतर की तुलना में कहीं अधिक है। मैं उत्सुक हूं कि किसी ने भी हार्डवेयर या लिखित सॉफ्टवेयर को किसी भी प्रकार के दशमलव पर कुशलतापूर्वक संचालित करने के लिए बनाया है, क्योंकि न तो हार्डवेयर और न ही सॉफ़्टवेयर में कुशल कार्यान्वयन के लिए उत्तरदायी होगा।
सुपरकैट

121

अन्य सही उत्तरों के अलावा, आप फ्लोटिंग-पॉइंट अंकगणित की समस्याओं से बचने के लिए अपने मूल्यों को बढ़ाने पर विचार कर सकते हैं।

उदाहरण के लिए:

var result = 1.0 + 2.0;     // result === 3.0 returns true

... के बजाय:

var result = 0.1 + 0.2;     // result === 0.3 returns false

अभिव्यक्ति जावास्क्रिप्ट में 0.1 + 0.2 === 0.3लौटती falseहै, लेकिन फ़्लोटिंग-पॉइंट में सौभाग्य से पूर्णांक अंकगणित सटीक है, इसलिए दशमलव प्रतिनिधित्व त्रुटियों को स्केलिंग से बचा जा सकता है।

एक व्यावहारिक उदाहरण के रूप में, फ्लोटिंग-पॉइंट की समस्याओं से बचने के लिए जहां सटीकता सर्वोपरि है, पैसे को संभालने के लिए 1 की सिफारिश की जाती है क्योंकि एक पूर्णांक के रूप में सेंट की संख्या का प्रतिनिधित्व करते हैं: डॉलर के 2550बजाय सेंट 25.50


1 डगलस क्रॉकफोर्ड: जावास्क्रिप्ट: द गुड पार्ट्स : अपेंडिक्स ए - अव्वल पार्ट्स (पृष्ठ 105)


3
समस्या यह है कि रूपांतरण स्वयं गलत है। 16.08 * 100 = 1607.9999999999998 क्या हमें संख्या को विभाजित करने और अलग से परिवर्तित करने का सहारा लेना होगा (जैसे कि 16 * 100 + 08 = 1608)?
जेसन

38
यहां समाधान यह है कि पूर्णांक में अपनी सभी गणनाएं करें फिर अपने अनुपात (इस मामले में 100) से विभाजित करें और केवल डेटा प्रस्तुत करते समय गोल करें। यह सुनिश्चित करेगा कि आपकी गणना हमेशा सटीक होगी।
डेविड ग्रैनाडो

15
बस थोड़ा सा नाइटपिक करने के लिए: पूर्णांक अंकगणित फ्लोटिंग-पॉइंट में केवल एक बिंदु तक (सटीक इरादा) होता है। यदि संख्या 0x1p53 से अधिक है (जावा 7 के हेक्साडेसिमल फ्लोटिंग पॉइंट नोटेशन, = 9007199254740992 पर उपयोग करने के लिए), तो ulp उस बिंदु पर 2 है और इसलिए 0x1p53 + 1 को 0x1p53 (और 0x1p53 + 3 को 0x1p53) तक गोल किया गया है। 4, क्योंकि गोल-से भी)। :-D लेकिन निश्चित रूप से, यदि आपकी संख्या 9 क्वाड्रिलियन से कम है, तो आपको ठीक होना चाहिए। :-P
क्रिस जस्टर-युवा

2
जेसन, आपको केवल परिणाम (int) (16.08 * 100 + 0.5) को गोल करना चाहिए
मिखाइल सेमेनोव

@CodyBugstein " तो आप कैसे प्राप्त करते हैं? .1 + .2 को दिखाने के लिए .3? " दशमलव को रखने के लिए एक कस्टम प्रिंट फ़ंक्शन लिखें जहां आप इसे चाहते हैं।
रॉनजॉन

113

मेरा उत्तर काफी लंबा है, इसलिए मैंने इसे तीन खंडों में विभाजित किया है। चूंकि सवाल फ्लोटिंग पॉइंट गणित के बारे में है, इसलिए मैंने इस बात पर जोर दिया है कि मशीन वास्तव में क्या करती है। मैंने इसे डबल (64 बिट) सटीक करने के लिए विशिष्ट भी बनाया है, लेकिन तर्क किसी भी फ्लोटिंग पॉइंट अंकगणित पर समान रूप से लागू होता है।

प्रस्तावना

एक आईईईई 754 डबल परिशुद्धता द्विआधारी फ्लोटिंग प्वाइंट प्रारूप (binary64) नंबर फार्म की एक संख्या का प्रतिनिधित्व

मान = (-1) ^ s * (1.m 51 m 50 ... m 2 m 1 m 0 ) 2 * 2 e-1023

64 बिट्स में:

  • पहला बिट साइन बिट है : 1यदि संख्या नकारात्मक है, 0अन्यथा 1
  • अगले 11 बिट्स प्रतिपादक हैं , जिसकी ऑफसेट 1023 है। दूसरे शब्दों में, दो-सटीक संख्या से घातांक बिट्स को पढ़ने के बाद, 1023 को दो की शक्ति प्राप्त करने के लिए घटाया जाना चाहिए।
  • शेष 52 बिट्स महत्व (या मंटिसा) हैं। मंटिसा में, एक 'निहित' 1.हमेशा 2 छोड़ दिया जाता है क्योंकि किसी भी द्विआधारी मूल्य का सबसे महत्वपूर्ण बिट होता है 1

1 - IEEE 754 एक हस्ताक्षरित शून्य की अवधारणा के लिए अनुमति देता है - +0और -0अलग तरीके से व्यवहार किया जाता है: 1 / (+0)सकारात्मक अनंत है; 1 / (-0)नकारात्मक अनंत है। शून्य मानों के लिए, मंटिसा और प्रतिपादक बिट्स सभी शून्य हैं। नोट: शून्य मान (+0 और -0) स्पष्ट रूप से २ के रूप में वर्गीकृत नहीं किए गए हैं ।

2 - यह असामान्य संख्याओं के लिए मामला नहीं है , जिसमें शून्य (और एक निहित 0.) का ऑफसेट एक्सपोनेंट है । असामान्य दोहरी सटीक संख्याओं की श्रेणी d मिनट है। | X | ≤ d अधिकतम , जहाँ d मिनट (सबसे छोटा अभाज्य नॉनजो संख्या) 2 -1023 है - 51 ( -3 4.94 * 10 -324 ) और d अधिकतम (सबसे बड़ा अपभ्रंश संख्या, जिसके लिए mantissa पूरी तरह से शामिल है 1) 2 -1023 है। + 1 - 2 -1023 - 51 ( - 2.225 * 10 -308 )।


बाइनरी के लिए एक डबल परिशुद्धता संख्या की ओर मुड़ते हुए

कई ऑनलाइन कन्वर्टर्स द्विआधारी सटीक फ्लोटिंग पॉइंट नंबर को बाइनरी में परिवर्तित करने के लिए मौजूद हैं (उदाहरण के लिए बाइनरीकॉनवर्ट डॉट कॉम पर ), लेकिन यहां कुछ सस्पेंशन C # कोड है, जिससे डबल सटीक संख्या के लिए IEEE 754 प्रतिनिधित्व प्राप्त किया जा सके (मैं कॉलन के साथ तीन भागों को अलग करता हूं :) :

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

बिंदु पर पहुंचना: मूल प्रश्न

(TL के लिए नीचे की ओर छोड़ें; DR संस्करण)

कैटो जॉनसन (प्रश्न पूछने वाला) ने पूछा कि 0.1 + 0.2! = 0.3 क्यों।

बाइनरी में लिखा गया है (तीन भागों को अलग करने वाले कॉलन के साथ), मूल्यों का IEEE 754 प्रतिनिधित्व हैं:

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

ध्यान दें कि मंटिसा के आवर्ती अंकों से बना है 0011। यह वह जगह है कुंजी 0.1, 0.2 और 0.3 बाइनरी में नहीं दर्शाया जा सकता - क्यों गणना के लिए किसी भी त्रुटि है करने के लिए ठीक एक में परिमित में ठीक किया जा सकता है किसी भी अधिक से अधिक 1/9 द्विआधारी बिट्स की संख्या, 1/3 या 1/7 दशमलव अंक

यह भी ध्यान दें कि हम 52 में घातांक में शक्ति को कम कर सकते हैं और बाइनरी प्रतिनिधित्व में बिंदु को 52 स्थानों पर दाईं ओर शिफ्ट कर सकते हैं (जैसे 10 -3 * 1.23 == 10 -5 * 123)। यह तब हमें द्विआधारी प्रतिनिधित्व का सटीक मूल्य के रूप में प्रतिनिधित्व करने में सक्षम बनाता है जो इसे * 2 पी के रूप में दर्शाता है । जहाँ 'a' पूर्णांक है।

घातांक को दशमलव में बदलना, ऑफसेट को हटाना, और निहित 1(वर्ग कोष्ठक में) को फिर से जोड़ना , 0.1 और 0.2:

0.1 => 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 => 2^-3 * [1].1001100110011001100110011001100110011001100110011010
or
0.1 => 2^-56 * 7205759403792794 = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794 = 0.200000000000000011102230246251565404236316680908203125

दो संख्याओं को जोड़ने के लिए, प्रतिपादक को एक समान होना चाहिए, अर्थात:

0.1 => 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 => 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum =  2^-3 * 10.0110011001100110011001100110011001100110011001100111
or
0.1 => 2^-55 * 3602879701896397  = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794  = 0.200000000000000011102230246251565404236316680908203125
sum =  2^-55 * 10808639105689191 = 0.3000000000000000166533453693773481063544750213623046875

चूँकि योग 2 n * 1 का नहीं है । {{bbb} हम एक-एक करके घातांक बढ़ाते हैं और प्राप्त करने के लिए दशमलव ( बाइनरी ) बिंदु को स्थानांतरित करते हैं:

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)
    = 2^-54 * 5404319552844595.5 = 0.3000000000000000166533453693773481063544750213623046875

मंटिसा में अब 53 बिट्स हैं (ऊपर की लाइन में 53 वां वर्ग ब्रैकेट में है)। IEEE 754 के लिए डिफ़ॉल्ट राउंडिंग मोड ' राउंड टू निकटतम ' है - अर्थात यदि संख्या x दो मानों के बीच a और b के बीच आती है , तो वह मान जहां कम से कम महत्वपूर्ण बिट शून्य है।

a = 2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875
  = 2^-2  * 1.0011001100110011001100110011001100110011001100110011

x = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)

b = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
  = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

ध्यान दें कि और बी केवल अंतिम बिट में भिन्न होते हैं; ...0011+ 1= ...0100। इस स्थिति में, शून्य के सबसे कम महत्वपूर्ण बिट के साथ मान बी है , इसलिए योग है:

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
    = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

जबकि बाइनरी का प्रतिनिधित्व 0.3 है:

0.3 => 2^-2  * 1.0011001100110011001100110011001100110011001100110011
    =  2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875

जो केवल 0.1 और 0.2 के योग के द्विआधारी प्रतिनिधित्व से 2 -54 तक भिन्न होता है ।

0.1 और 0.2 का द्विआधारी प्रतिनिधित्व IEEE 754 द्वारा स्वीकार्य संख्याओं का सबसे सटीक प्रतिनिधित्व है। इन प्रतिनिधित्व के अलावा, डिफ़ॉल्ट गोलाई मोड के कारण, एक मूल्य में परिणाम होता है जो केवल सबसे कम-महत्वपूर्ण-बिट में भिन्न होता है।

टी एल; डॉ

0.1 + 0.2IEEE 754 बाइनरी प्रतिनिधित्व (तीन भागों को अलग करने वाले कॉलोनों के साथ) में लिखना और इसकी तुलना करना 0.3, यह है (मैंने अलग-अलग बिट्स को वर्ग कोष्ठक में रखा है):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

दशमलव में परिवर्तित, ये मान निम्न हैं:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

अंतर वास्तव में 2 -54 है , जो कि मूल मानों की तुलना में ~ 5.5511151231258 × 10 -17 - तुच्छ (कई अनुप्रयोगों के लिए) है।

फ्लोटिंग पॉइंट नंबर के अंतिम कुछ बिट्स की तुलना करना स्वाभाविक रूप से खतरनाक है, क्योंकि कोई भी व्यक्ति जो " फ्लोटिंग-पॉइंट अरिथमेटिक के बारे में प्रसिद्ध है " (जो इस उत्तर के सभी प्रमुख हिस्सों को कवर करता है) के बारे में प्रसिद्ध है।

अधिकांश कैलकुलेटर अतिरिक्त का उपयोग गार्ड अंक इस समस्या है, जो कैसे है चारों ओर पाने के लिए 0.1 + 0.2देना होगा 0.3अंतिम कुछ बिट्स गोल कर रहे हैं:।


14
मेरे उत्तर को पोस्ट करने के कुछ ही समय बाद वोट दिया गया। मैंने तब से कई बदलाव किए हैं (बाइनरी में 0.1 और 0.2 लिखते समय स्पष्ट रूप से आवर्ती बिट्स को ध्यान में रखते हुए, जिसे मैंने मूल में छोड़ दिया था)। इस अवसर पर कि डाउन-वोटर इसे देखता है, क्या आप मुझे कुछ प्रतिक्रिया दे सकते हैं ताकि मैं अपने उत्तर में सुधार कर सकूं? मुझे लगता है कि आईईईई 754 में राशि के उपचार के बाद से मेरा उत्तर कुछ नया जोड़ता है, अन्य उत्तरों में उसी तरह से कवर नहीं किया गया है। जबकि "प्रत्येक कंप्यूटर वैज्ञानिक को क्या पता होना चाहिए ..." कुछ समान सामग्री को शामिल करता है, मेरा जवाब विशेष रूप से 0.1 + 0.2 के मामले से संबंधित है।
वाई हा ली

57

कंप्यूटर में संग्रहीत फ़्लोटिंग पॉइंट संख्या में दो भाग होते हैं, एक पूर्णांक और एक घातांक जिसे आधार को पूर्णांक भाग से गुणा और गुणा किया जाता है।

यदि कंप्यूटर बेस 10 में काम कर रहा था, 0.1होगा 1 x 10⁻¹, 0.2होगा 2 x 10⁻¹, और 0.3होगा 3 x 10⁻¹। पूर्णांक गणित आसान और सटीक है, इसलिए जोड़ना 0.1 + 0.2स्पष्ट रूप से परिणाम देगा 0.3

कंप्यूटर आमतौर पर बेस 10 में काम नहीं करते हैं, वे बेस 2 में काम करते हैं। आप अभी भी कुछ मूल्यों के लिए सटीक परिणाम प्राप्त कर सकते हैं, उदाहरण के 0.5लिए 1 x 2⁻¹और 0.25है 1 x 2⁻², और उन्हें परिणाम में जोड़ रहा है 3 x 2⁻², या 0.75। बिल्कुल सही।

समस्या उन संख्याओं के साथ आती है जिन्हें आधार 10 में बिल्कुल दर्शाया जा सकता है, लेकिन आधार 2 में नहीं। उन संख्याओं को अपने निकटतम समकक्ष पर गोल करने की आवश्यकता है। बहुत सामान्य IEEE 64-बिट फ्लोटिंग पॉइंट फॉर्मेट को मानकर, निकटतम संख्या 0.1है 3602879701896397 x 2⁻⁵⁵, और निकटतम संख्या 0.2है 7205759403792794 x 2⁻⁵⁵; उन्हें एक साथ जोड़ने या उनमें से 10808639105689191 x 2⁻⁵⁵एक सटीक दशमलव मान होता है 0.3000000000000000444089209850062616169452667236328125। फ्लोटिंग पॉइंट नंबर आमतौर पर प्रदर्शन के लिए गोल होते हैं।


2
@ मार्क आपको इस स्पष्ट स्पष्टीकरण के लिए धन्यवाद देता है लेकिन फिर सवाल उठता है कि 0.1 + 0.4 बिल्कुल 0.5 तक क्यों जोड़ता है (पायथन 3 में कम से कम)। पायथन 3 में फ्लोट्स का उपयोग करते समय समानता की जांच करने का सबसे अच्छा तरीका क्या है?
पचेगूर २०'१

2
@ user2417881 IEEE फ़्लोटिंग पॉइंट ऑपरेशंस में हर ऑपरेशन के लिए राउंडिंग नियम होते हैं, और कभी-कभी राउंडिंग एक सटीक उत्तर भी उत्पन्न कर सकता है जब दो नंबर थोड़े से बंद होते हैं। विवरण एक टिप्पणी के लिए बहुत लंबा है और मैं वैसे भी उनमें विशेषज्ञ नहीं हूं। जैसा कि आप इस उत्तर में देखते हैं 0.5 कुछ गिने-चुने दशमलव में से एक है जिसे बाइनरी में दर्शाया जा सकता है, लेकिन यह सिर्फ एक संयोग है। समानता परीक्षण के लिए stackoverflow.com/questions/5595425/… देखें ।
मार्क रैनसम

1
@ user2417881 आपके सवाल ने मुझे बहुत परेशान किया इसलिए मैंने इसे एक पूर्ण प्रश्न और उत्तर में बदल दिया: stackoverflow.com/q/48374522/5987
मार्क रैनसम

47

फ्लोटिंग पॉइंट राउंडिंग एरर। से क्या हर कंप्यूटर वैज्ञानिक चाहिए नो फ्लोटिंग प्वाइंट अंकगणित के बारे में :

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


33

मेरा समाधान:

function add(a, b, precision) {
    var x = Math.pow(10, precision || 2);
    return (Math.round(a * x) + Math.round(b * x)) / x;
}

परिशुद्धता उन अंकों की संख्या को संदर्भित करता है जिन्हें आप दशमलव बिंदु के अलावा संरक्षित करना चाहते हैं।


30

बहुत सारे अच्छे उत्तर पोस्ट किए गए हैं, लेकिन मैं एक और अपील करना चाहूंगा।

सभी संख्याओं को फ़्लोट्स / डबल्स के माध्यम से नहीं दिखाया जा सकता है। उदाहरण के लिए, संख्या "0.2" को IEEE754 फ्लोट पॉइंट मानक में एकल परिशुद्धता में "0.200000003" के रूप में दर्शाया जाएगा।

हुड के तहत वास्तविक संख्या की दुकान के लिए मॉडल के रूप में फ्लोट संख्या का प्रतिनिधित्व करते हैं

यहां छवि विवरण दर्ज करें

भले ही आप 0.2आसानी से टाइप कर सकते हैं, FLT_RADIXऔर DBL_RADIX2 है; एफपीयू वाले कंप्यूटर के लिए 10 नहीं जो "बाइनरी फ्लोटिंग-पॉइंट अंकगणित (आईएसओ / आईईईई स्टैड 754-1985) के लिए आईईईई मानक" का उपयोग करता है।

इसलिए ऐसी संख्याओं का सही प्रतिनिधित्व करना थोड़ा कठिन है। यहां तक ​​कि अगर आप इस चर को स्पष्ट रूप से किसी भी मध्यवर्ती गणना के बिना निर्दिष्ट करते हैं।


28

इस प्रसिद्ध डबल सटीक प्रश्न से संबंधित कुछ आंकड़े।

0.1 (0.1 से 100 तक) के चरण का उपयोग करते हुए सभी मान ( a + b ) जोड़ते समय हमारे पास ~ 15% सटीक त्रुटि की संभावना है । ध्यान दें कि त्रुटि थोड़ा बड़ा या छोटा मान हो सकता है। यहाँ कुछ उदाहरण हैं:

0.1 + 0.2 = 0.30000000000000004 (BIGGER)
0.1 + 0.7 = 0.7999999999999999 (SMALLER)
...
1.7 + 1.9 = 3.5999999999999996 (SMALLER)
1.7 + 2.2 = 3.9000000000000004 (BIGGER)
...
3.2 + 3.6 = 6.800000000000001 (BIGGER)
3.2 + 4.4 = 7.6000000000000005 (BIGGER)

0.1 (100 से 0.1 तक) के चरण का उपयोग करके सभी मानों ( a - b जहाँ a> b ) को घटाते समय हमारे पास ~ 34% सटीक त्रुटि होने की संभावना होती है । यहाँ कुछ उदाहरण हैं:

0.6 - 0.2 = 0.39999999999999997 (SMALLER)
0.5 - 0.4 = 0.09999999999999998 (SMALLER)
...
2.1 - 0.2 = 1.9000000000000001 (BIGGER)
2.0 - 1.9 = 0.10000000000000009 (BIGGER)
...
100 - 99.9 = 0.09999999999999432 (SMALLER)
100 - 99.8 = 0.20000000000000284 (BIGGER)

* 15% और 34% वास्तव में बहुत बड़े हैं, इसलिए हमेशा BigDecimal का उपयोग करें जब परिशुद्धता बड़ा महत्व है। 2 दशमलव अंकों (चरण 0.01) के साथ स्थिति थोड़ी और बिगड़ती है (18% और 36%)।


28

नहीं, टूटा नहीं है, लेकिन अधिकांश दशमलव अंशों का अनुमान लगाना चाहिए

सारांश

चल बिन्दु गणित है सटीक, दुर्भाग्य से, यह अच्छी तरह से हमारी सामान्य आधार -10 नंबर प्रतिनिधित्व के साथ मेल नहीं खाता, तो यह बदल जाता है बाहर हम अक्सर यह इनपुट कि थोड़ा हम क्या लिखा से बंद है दे रहे हैं।

यहां तक ​​कि साधारण संख्या जैसे 0.01, 0.02, 0.03, 0.04 ... 0.24 बाइनरी अंशों के समान प्रतिनिधित्व योग्य नहीं हैं। यदि आप 0.01, .02, .03 ... को गिनते हैं, तो 0.25 तक नहीं मिलने पर आपको आधार 2 में पहला अंश अभ्यावेदन मिलेगा । यदि आपने कोशिश की है कि FP का उपयोग करते हुए, आपका 0.01 थोड़ा बंद हो जाएगा, तो उनमें से 25 को एक अच्छा सटीक 0.25 तक जोड़ने का एकमात्र तरीका गार्ड बिट्स और गोलाई से संबंधित कार्य-कारण की लंबी श्रृंखला की आवश्यकता होगी। यह अनुमान लगाना मुश्किल है कि हम अपने हाथों को फेंक दें और कहें कि "एफपी अक्षम्य है", लेकिन यह वास्तव में सच नहीं है।

हम लगातार एफपी हार्डवेयर को कुछ देते हैं जो बेस 10 में सरल लगता है लेकिन बेस 2 में एक दोहराव अंश है।

ये कैसे हुआ?

जब हम दशमलव में लिखते हैं, तो हर अंश (विशेष रूप से, हर समाप्ति दशमलव) प्रपत्र की एक तर्कसंगत संख्या होती है

           a / (2 n x 5 m )

बाइनरी में, हमें केवल 2 एन टर्म मिलता है , जो है:

           ए / 2 एन

तो दशमलव में, हम का प्रतिनिधित्व नहीं कर सकते हैं 1 / 3 । क्योंकि आधार 10 में 2 एक प्रमुख कारक के रूप में शामिल है, हर संख्या जिसे हम बाइनरी अंश के रूप में लिख सकते हैं, आधार 10 अंश के रूप में भी लिखा जा सकता है। हालांकि, शायद ही कुछ भी हम आधार 10 अंश के रूप में लिखते हैं बाइनरी में प्रतिनिधित्व योग्य है। 0.01, 0.02, 0.03 ... 0.99 की सीमा में, केवल तीन संख्याओं को हमारे FP प्रारूप में दर्शाया जा सकता है: 0.25, 0.50 और 0.75, क्योंकि वे 1/4, 1/2 और 3/4 हैं, सभी संख्याएँ केवल 2 एन टर्म का उपयोग करते हुए एक प्रमुख कारक के साथ ।

आधार में 10 हम का प्रतिनिधित्व नहीं कर सकते हैं 1 / 3 । लेकिन बाइनरी में, हम ऐसा नहीं कर सकते 1 / 10 या 1 / 3

तो जबकि हर बाइनरी अंश दशमलव में लिखा जा सकता है, रिवर्स सच नहीं है। और वास्तव में अधिकांश दशमलव अंश द्विआधारी में दोहराते हैं।

इससे निपटना

डेवलपर्स को आमतौर पर <epsilon तुलना करने के लिए निर्देश दिया जाता है , बेहतर सलाह हो सकती है कि वे इंटीग्रल वैल्यू (C लाइब्रेरी में: राउंड) (और राउंडफ (), यानी, FP प्रारूप में रहें) और फिर तुलना करें। एक विशिष्ट दशमलव अंश की लंबाई में उत्पादन के साथ अधिकांश समस्याओं का हल होता है।

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

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

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

निष्कर्ष

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


सभी मामलों में तुलना समस्या को हल करने के लिए निकटतम पूर्णांक तक गोलाई सुरक्षित तरीका नहीं है। 0.4999998 और 0.500001 विभिन्न पूर्णांकों के लिए राउंड, इसलिए प्रत्येक राउंडिंग कट-पॉइंट के आसपास एक "डेंजर ज़ोन" है। (मुझे पता है कि उन दशमलव तार शायद IEEE बाइनरी फ़्लोट के रूप में प्रतिनिधित्व करने योग्य नहीं हैं।)
पीटर कॉर्ड्स

1
इसके अलावा, भले ही फ्लोटिंग पॉइंट "विरासत" प्रारूप हो, यह बहुत अच्छी तरह से डिज़ाइन किया गया है। मुझे इस बात का कुछ भी पता नहीं है कि अब इसे फिर से डिजाइन करने पर कोई भी बदल जाएगा। जितना अधिक मैं इसके बारे में सीखता हूं, मुझे लगता है कि यह वास्तव में अच्छी तरह से डिज़ाइन किया गया है। उदाहरण के लिए पक्षपाती प्रतिपादक का अर्थ है निरंतर बाइनरी फ़्लोट्स में लगातार पूर्णांक निरूपण होता है, इसलिए आप nextafter()IEEE फ़्लोट के बाइनरी प्रतिनिधित्व पर पूर्णांक वृद्धि या गिरावट के साथ लागू कर सकते हैं । इसके अलावा, आप फ़्लोटर्स की तुलना पूर्णांक के रूप में कर सकते हैं और सही उत्तर प्राप्त कर सकते हैं, जब वे नकारात्मक हों (साइन-परिमाण बनाम 2 के पूरक के कारण)।
पीटर कॉर्ड्स

मैं असहमत हूं, झांकियों को दशमलव के रूप में संग्रहीत किया जाना चाहिए न कि बाइनरी और सभी समस्याओं को हल किया जाता है।
रॉनन फिस्टिंगर

" X / (2 ^ n + 5 ^ n) " नहीं होना चाहिए " x / (2 ^ n * 5 ^ n) "?
वाई हा ली

@RonenFestinger - 1/3 के बारे में क्या?
स्टीफन C

19

क्या आपने डक्ट टेप समाधान की कोशिश की?

यह निर्धारित करने का प्रयास करें कि त्रुटियां कब होती हैं और उन्हें कम करके ठीक करें यदि कथन, तो यह सुंदर नहीं है, लेकिन कुछ समस्याओं के लिए यह एकमात्र समाधान है और यह उनमें से एक है।

 if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
                    else { return n * 0.1 + 0.000000000000001 ;}    

मुझे सी # में एक वैज्ञानिक सिमुलेशन परियोजना में एक ही समस्या थी, और मैं आपको बता सकता हूं कि यदि आप तितली के प्रभाव को अनदेखा करते हैं, तो यह एक बड़े वसा वाले ड्रैगन की ओर जाता है और आपको एक ** में काटता है।


19

सबसे अच्छा समाधान प्रदान करने के लिए मैं कह सकता हूं कि मैंने निम्नलिखित विधि की खोज की है:

parseFloat((0.1 + 0.2).toFixed(10)) => Will return 0.3

मुझे समझाएं कि यह सबसे अच्छा समाधान क्यों है। जैसा कि अन्य लोगों ने उपर्युक्त उत्तरों में बताया है कि समस्या को हल करने के लिए जावास्क्रिप्ट (toFixed) फ़ंक्शन का उपयोग करने के लिए तैयार उपयोग करना एक अच्छा विचार है। लेकिन सबसे अधिक संभावना है कि आप कुछ समस्याओं से सामना करेंगे।

कल्पना कीजिए कि आप दो फ़्लोट संख्याएँ जोड़ने जा रहे हैं जैसे 0.2और 0.7यहाँ यह है 0.2 + 0.7 = 0.8999999999999999:।

आपका अपेक्षित परिणाम 0.9यह था कि इसका मतलब है कि आपको इस मामले में 1 अंकों की सटीकता के साथ परिणाम की आवश्यकता है। तो आप का उपयोग किया जाना चाहिए, (0.2 + 0.7).tofixed(1) लेकिन आप उदाहरण के लिए दिए गए नंबर पर निर्भर करता है क्योंकि आप toFixed () के लिए एक निश्चित पैरामीटर नहीं दे सकते

`0.22 + 0.7 = 0.9199999999999999`

इस उदाहरण में आपको 2 अंकों की सटीकता की आवश्यकता है toFixed(2), इसलिए यह होना चाहिए , इसलिए हर दिए गए फ्लोट नंबर को फिट करने के लिए क्या पैरामेटर होना चाहिए?

आप कह सकते हैं कि यह हर स्थिति में 10 हो सकता है:

(0.2 + 0.7).toFixed(10) => Result will be "0.9000000000"

अरे नहीं! आप 9 के बाद उन अवांछित शून्य के साथ क्या करने जा रहे हैं? अपनी इच्छानुसार इसे बनाने के लिए इसे फ्लोट में बदलने का समय है:

parseFloat((0.2 + 0.7).toFixed(10)) => Result will be 0.9

अब जब आपको समाधान मिल गया है, तो इसे इस तरह से एक फ़ंक्शन के रूप में पेश करना बेहतर है:

function floatify(number){
           return parseFloat((number).toFixed(10));
        }

आइए इसे स्वयं आज़माएँ:

function floatify(number){
       return parseFloat((number).toFixed(10));
    }
 
function addUp(){
  var number1 = +$("#number1").val();
  var number2 = +$("#number2").val();
  var unexpectedResult = number1 + number2;
  var expectedResult = floatify(number1 + number2);
  $("#unexpectedResult").text(unexpectedResult);
  $("#expectedResult").text(expectedResult);
}
addUp();
input{
  width: 50px;
}
#expectedResult{
color: green;
}
#unexpectedResult{
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="number1" value="0.2" onclick="addUp()" onkeyup="addUp()"/> +
<input id="number2" value="0.7" onclick="addUp()" onkeyup="addUp()"/> =
<p>Expected Result: <span id="expectedResult"></span></p>
<p>Unexpected Result: <span id="unexpectedResult"></span></p>

आप इसे इस तरह से उपयोग कर सकते हैं:

var x = 0.2 + 0.7;
floatify(x);  => Result: 0.9

जैसा कि W3SCHOOLS बताता है कि एक और उपाय भी है, आप उपरोक्त समस्या को हल करने के लिए गुणा और भाग कर सकते हैं:

var x = (0.2 * 10 + 0.1 * 10) / 10;       // x will be 0.3

ध्यान रखें कि (0.2 + 0.1) * 10 / 10यह बिल्कुल समान है, हालांकि काम नहीं करेगा! मैं पहला समाधान पसंद करता हूं क्योंकि मैं इसे एक फ़ंक्शन के रूप में लागू कर सकता हूं जो इनपुट फ्लोट को सटीक आउटपुट फ्लोट में परिवर्तित करता है।


इसने मुझे असली सिरदर्द बना दिया। मैं 12 फ्लोट संख्याओं का योग करता हूं, फिर योग और उन संख्याओं का औसत दिखाता हूं। toFixed () का उपयोग करके 2 संख्याओं के योग को ठीक किया जा सकता है, लेकिन जब कई संख्याओं का योग महत्वपूर्ण होता है।
नूरगड्डी मुस्तपेय

@Nuryagdy Mustapayev मुझे आपका इरादा नहीं मिला, जैसा कि मैंने परीक्षण किया था इससे पहले कि आप 12 फ्लोट संख्याओं का योग कर सकते हैं, फिर परिणाम पर फ़्लोटिफ़ाइ () फ़ंक्शन का उपयोग करें, फिर आप जो चाहें उस पर काम करें, मैंने इसका उपयोग करते हुए कोई मुद्दा नहीं देखा।
मोहम्मद मुसावी

मैं सिर्फ अपनी स्थिति में कह रहा हूं जहां मेरे पास लगभग 20 पैरामीटर और 20 सूत्र हैं जहां प्रत्येक सूत्र का परिणाम दूसरों पर निर्भर करता है यह समाधान मदद नहीं करता है।
नूरगड्डी मुस्तयपेव

16

वे अजीब संख्याएँ दिखाई देती हैं क्योंकि कंप्यूटर बाइनरी (बेस 2) नंबर सिस्टम का उपयोग गणना उद्देश्यों के लिए करते हैं, जबकि हम दशमलव (बेस 10) का उपयोग करते हैं।

बहुसंख्यक भिन्न संख्याएँ हैं जिनका बाइनरी या दशमलव या दोनों में सटीक प्रतिनिधित्व नहीं किया जा सकता है। परिणाम - एक गोल (लेकिन सटीक) संख्या परिणाम।


मैं आपके दूसरे पैराग्राफ को बिल्कुल नहीं समझता।
ना

1
@ नहीं मैं दूसरे पैराग्राफ का अनुवाद करूंगा "अधिकांश अंशों को दशमलव या बाइनरी में बिल्कुल नहीं दर्शाया जा सकता है । इसलिए अधिकांश परिणाम गोल किए जाएंगे - हालांकि वे प्रतिनिधित्व में निहित बिट्स / अंकों की संख्या के लिए अभी भी सटीक होंगे। उपयोग किया जा रहा है।"
स्टीव समिट

15

इस प्रश्न के कई डुप्लिकेट विशिष्ट संख्याओं पर फ़्लोटिंग पॉइंट राउंडिंग के प्रभावों के बारे में पूछते हैं। व्यवहार में, इसके बारे में सिर्फ पढ़ने के बजाय ब्याज की गणना के सटीक परिणामों को देखकर यह महसूस करना आसान है कि यह कैसे काम करता है। इस तरह के एक परिवर्तित रूप में - कुछ भाषाओं में है कि कुछ करने के तरीके प्रदान करते हैं floatया doubleकरने के लिए BigDecimalजावा में।

चूँकि यह एक भाषा-अज्ञेय प्रश्न है, इसलिए इसे भाषा-अज्ञेयवादी उपकरण की आवश्यकता होती है, जैसे दशमलव से फ़्लोटिंग-पॉइंट कनवर्टर तक

इसे प्रश्न में संख्याओं पर लागू करना, युगल के रूप में माना जाता है:

0.1 धर्मान्तरित 0.1000000000000000055511151231257827021181583404541015625,

0.2 धर्मान्तरित 0.200000000000000011102230246251565404236316680908203125,

0.3 में परिवर्तित होता है 0.29999999999999988887769753748434595763683319091796875, और

0.3000000000000000000004 0.3000000000000000444089209850062616169452667236328125 में कनवर्ट करता है।

पहले दो नंबरों को मैन्युअल रूप से या पूर्ण परिशुद्धता कैलकुलेटर जैसे एक दशमलव कैलकुलेटर में जोड़ना , वास्तविक इनपुट का सटीक योग दिखाता है 0.3000000000000000166533453693773481063544750213623046875।

यदि इसे 0.3 के बराबर कर दिया गया तो गोलाई त्रुटि 0.0000000000000000277555756156289135105907917022705078125 होगी। ०.३००००००००००००००००००४ के बराबर गोलाई भी ४.००००००००००००००००००75५५५५५५५५६५६५६६79२79 ९ ३५० ९ ० ९ ०१70०70०50५५०125५५ पर गोलाई देने की त्रुटि देता है। गोल-टू-सम टाई ब्रेकर लागू होता है।

फ़्लोटिंग पॉइंट कनवर्टर पर लौटना, 0.30000000000000004 के लिए कच्चा हेक्साडेसिमल 3fd3333333333334 है, जो एक सम अंक में समाप्त होता है और इसलिए सही परिणाम है।


2
जिस व्यक्ति के संपादन में मैं अभी-अभी वापस आया हूं: मैं कोड उद्धरण को कोड के लिए उपयुक्त मानता हूं। यह जवाब, भाषा-तटस्थ होने के नाते, इसमें कोई भी उद्धृत कोड शामिल नहीं है। अंग्रेजी वाक्यों में संख्याओं का उपयोग किया जा सकता है और यह उन्हें कोड में नहीं बदलता है।
पेट्रिशिया शहनाहन

यह संभावना है कि क्यों किसी ने कोड के रूप में आपके नंबर को स्वरूपित किया - प्रारूपण के लिए नहीं, बल्कि पठनीयता के लिए।
वाई हा ली

... भी, यहां तक कि दौर को दर्शाता है द्विआधारी प्रतिनिधित्व, नहीं दशमलव प्रतिनिधित्व। देखें इस उदाहरण के लिए या,, इस
वाई हा ली

@AiHaLee मैंने किसी भी दशमलव संख्या में विषम / केवल परीक्षण लागू नहीं किया, केवल हेक्साडेसिमल। एक हेक्साडेसिमल अंक तब भी है, और केवल अगर, इसके द्विआधारी विस्तार का सबसे कम महत्वपूर्ण शून्य है।
पेट्रीसिया शहनहान

14

यह देखते हुए कि किसी ने भी इसका उल्लेख नहीं किया है ...

कुछ उच्च स्तरीय भाषाएं जैसे पायथन और जावा बाइनरी फ्लोटिंग पॉइंट सीमाओं को दूर करने के लिए टूल के साथ आते हैं। उदाहरण के लिए:

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

    पैसे के साथ काम करते समय दशमलव बहुत अच्छा है: दस सेंट प्लस बीस सेंट हमेशा तीस सेंट हैं:

    >>> 0.1 + 0.2 == 0.3
    False
    >>> Decimal('0.1') + Decimal('0.2') == Decimal('0.3')
    True
    

    पायथन का decimalमॉड्यूल IEEE मानक 854-1987 पर आधारित है ।

  • पायथन का fractionsमॉड्यूल और अपाचे कॉमन की BigFractionक्लास । दोनों (numerator, denominator)जोड़े के रूप में तर्कसंगत संख्याओं का प्रतिनिधित्व करते हैं और वे दशमलव फ़्लोटिंग पॉइंट अंकगणित की तुलना में अधिक सटीक परिणाम दे सकते हैं।

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


14

क्या मैं सिर्फ जोड़ सकता हूं; लोग हमेशा इसे कंप्यूटर की समस्या मानते हैं, लेकिन अगर आप अपने हाथों (आधार 10) से गिनते हैं, तो आप (1/3+1/3=2/3)=trueतब तक नहीं मिल सकते जब तक कि आपके पास 0.333 ... 0.333 ... जोड़ने की अनंतता न हो ... बस (1/10+2/10)!==3/10आधार में समस्या के साथ २, आप इसे ०.३३३ + ०.३३३ = ०.६६६ पर छांटते हैं और शायद इसे ०.६६ which पर गोल करते हैं जो तकनीकी रूप से गलत भी होगा।

टर्नरी में गिनती, और तिहाई हालांकि एक समस्या नहीं है - शायद प्रत्येक हाथ पर 15 उंगलियों के साथ कुछ दौड़ पूछेंगे कि आपका दशमलव गणित क्यों टूट गया था ...


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

मनुष्य आधार 10 (दशमलव) के अलावा कई आधारों का उपयोग करता है, द्विआधारी वह है जिसका हम कंप्यूटिंग के लिए सबसे अधिक उपयोग करते हैं .. 'अच्छा कारण' यह है कि आप बस हर आधार में हर अंश का प्रतिनिधित्व नहीं कर सकते हैं ..

@RonenFestinger बाइनरी अंकगणित को कंप्यूटर पर लागू करना आसान है क्योंकि इसके लिए अंकों के साथ केवल आठ मूल संचालन की आवश्यकता होती है: $ $ $, $ b $ $ 0,1 में, $ सभी को आपको जानना आवश्यक है $ \ operatorname [xor} (a, b) $ और $ \ operatorname {cb} (a, b) $, जहाँ x एक्सक्लूसिव है या cb "कैरी बिट" है, जो $ 1 = 1 = b $ को छोड़कर सभी मामलों में $ 0 है, जिस स्थिति में हमारे पास है एक (वास्तव में सभी परिचालनों की संप्रेषणीयता आपको $ 2 $ मामलों को बचाती है और आपको केवल $ 6 $ नियमों की आवश्यकता होती है)। दशमलव विस्तार के लिए $ 10 \ गुना 11 $ (दशमलव संकेतन में) मामलों को संग्रहित करने और प्रत्येक बिट के लिए $ 10 $ अलग-अलग राज्यों और कैरी पर कचरे के भंडारण की आवश्यकता होती है।
Oskar Limka

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

9

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

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

यदि आपको अनंत सटीकता की आवश्यकता है (उदाहरण के लिए, संख्या, इसके कई छोटे स्टैंड-इन में से एक के बजाय), तो आपको इसके बजाय एक प्रतीकात्मक गणित कार्यक्रम लिखना या उपयोग करना चाहिए।

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


9

बस मज़े के लिए, मैंने मानक C99 की परिभाषाओं का पालन करते हुए, फ़्लोट्स के प्रतिनिधित्व के साथ खेला और मैंने नीचे कोड लिखा।

कोड 3 अलग-अलग समूहों में फ़्लोट्स के बाइनरी प्रतिनिधित्व को प्रिंट करता है

SIGN EXPONENT FRACTION

और इसके बाद यह एक राशि प्रिंट करता है, जब, पर्याप्त सटीकता के साथ अभिव्यक्त किया जाता है, तो यह मूल्य दिखाएगा जो वास्तव में हार्डवेयर में मौजूद है।

इसलिए जब आप लिखते हैं float x = 999..., तो संकलक उस संख्या को फ़ंक्शन द्वारा मुद्रित बिट प्रतिनिधित्व में बदल देगा, xxजैसे कि फ़ंक्शन द्वारा मुद्रित योगyy दी गई संख्या के बराबर हो।

वास्तव में, यह योग केवल एक अनुमान है। संख्या 999,999,999 के लिए संकलक फ्लोट संख्या 1,000,000,000 के बिट प्रतिनिधित्व में सम्मिलित करेगा

कोड के बाद मैं एक कंसोल सत्र संलग्न करता हूं, जिसमें मैं दोनों स्थिरांक (माइनस पीआई और 999999999) के लिए शब्दों की गणना करता हूं जो वास्तव में हार्डवेयर में मौजूद होते हैं, संकलक द्वारा सम्मिलित किए जाते हैं।

#include <stdio.h>
#include <limits.h>

void
xx(float *x)
{
    unsigned char i = sizeof(*x)*CHAR_BIT-1;
    do {
        switch (i) {
        case 31:
             printf("sign:");
             break;
        case 30:
             printf("exponent:");
             break;
        case 23:
             printf("fraction:");
             break;

        }
        char b=(*(unsigned long long*)x&((unsigned long long)1<<i))!=0;
        printf("%d ", b);
    } while (i--);
    printf("\n");
}

void
yy(float a)
{
    int sign=!(*(unsigned long long*)&a&((unsigned long long)1<<31));
    int fraction = ((1<<23)-1)&(*(int*)&a);
    int exponent = (255&((*(int*)&a)>>23))-127;

    printf(sign?"positive" " ( 1+":"negative" " ( 1+");
    unsigned int i = 1<<22;
    unsigned int j = 1;
    do {
        char b=(fraction&i)!=0;
        b&&(printf("1/(%d) %c", 1<<j, (fraction&(i-1))?'+':')' ), 0);
    } while (j++, i>>=1);

    printf("*2^%d", exponent);
    printf("\n");
}

void
main()
{
    float x=-3.14;
    float y=999999999;
    printf("%lu\n", sizeof(x));
    xx(&x);
    xx(&y);
    yy(x);
    yy(y);
}

यहाँ एक सांत्वना सत्र है जिसमें मैं हार्डवेयर में मौजूद फ्लोट के वास्तविक मूल्य की गणना करता हूँ। मैं bcमुख्य प्रोग्राम द्वारा आउटपुट किए गए शब्दों का योग प्रिंट करता था। एक अजगर replया कुछ समान में भी वह राशि सम्मिलित कर सकते हैं।

-- .../terra1/stub
@ qemacs f.c
-- .../terra1/stub
@ gcc f.c
-- .../terra1/stub
@ ./a.out
sign:1 exponent:1 0 0 0 0 0 0 fraction:0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1
sign:0 exponent:1 0 0 1 1 1 0 fraction:0 1 1 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0
negative ( 1+1/(2) +1/(16) +1/(256) +1/(512) +1/(1024) +1/(2048) +1/(8192) +1/(32768) +1/(65536) +1/(131072) +1/(4194304) +1/(8388608) )*2^1
positive ( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
-- .../terra1/stub
@ bc
scale=15
( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
999999999.999999446351872

बस। 999999999 का मूल्य वास्तव में है

999999999.999999446351872

आप यह भी देख सकते हैं bcकि -3.14 भी गड़बड़ा गया है। में एक scaleकारक सेट करने के लिए मत भूलना bc

प्रदर्शित योग वह है जो हार्डवेयर के अंदर होता है। यह मान आपके द्वारा गणना करने से आपके द्वारा निर्धारित पैमाने पर निर्भर करता है। मैंने scaleकारक को 15 पर सेट किया । गणितीय रूप से, अनंत परिशुद्धता के साथ, ऐसा लगता है कि यह 1,000,000,000 है।


5

इसे देखने का एक और तरीका: संख्याओं का प्रतिनिधित्व करने के लिए प्रयुक्त 64 बिट्स हैं। परिणाम के रूप में 2 से अधिक कोई रास्ता नहीं है ** 64 = 18,446,744,073,709,551,616 विभिन्न संख्याओं का सटीक प्रतिनिधित्व किया जा सकता है।

हालांकि, मैथ का कहना है कि पहले से ही 0 और 1. के बीच कई डेसीमल पहले से ही हैं, IEE 754 इनको 64 बिट्स का उपयोग करने के लिए एक एन्कोडिंग को परिभाषित करता है, जो कि अधिक बड़ी संख्या वाले स्थान और NaN और + / - इन्फिनिटी के लिए कुशलतापूर्वक उपयोग करता है, इसलिए सही प्रतिनिधित्व के साथ अंतराल के बीच अंतराल हैं। संख्या केवल अनुमानित है।

दुर्भाग्य से 0.3 अंतर में बैठता है।


4

आधार के साथ बेस दस में काम करने की कल्पना करें, कहते हैं, सटीकता के 8 अंक। आप जांचें कि क्या

1/3 + 2 / 3 == 1

और जानें कि यह रिटर्न false । क्यों? खैर, असली संख्या हमारे पास है

1/3 = 0.333 .... और 2/3 = 0.666 ...।

आठ दशमलव स्थानों पर घूमते हुए, हम प्राप्त करते हैं

0.33333333 + 0.66666666 = 0.99999999

जो निश्चित रूप से 1.00000000बिल्कुल अलग है 0.00000001


बिट्स की निश्चित संख्या के साथ बाइनरी नंबर के लिए स्थिति बिल्कुल अनुरूप है। वास्तविक संख्या के रूप में, हमारे पास है

1/10 = 0.0001100110011001100 ... (आधार 2)

तथा

1/5 = 0.0011001100110011001 ... (आधार 2)

अगर हम उन्हें सात बिट्स कहते हैं, तो हम मिलेंगे

0.0001100 + 0.0011001 = 0.0100101

दूसरी ओर,

3/10 = 0.01001100110011 ... (आधार 2)

जो, सात बिट्स को काट दिया गया है 0.0100110, और ये बिलकुल अलग हैं 0.0000001


सटीक स्थिति थोड़ी अधिक सूक्ष्म है क्योंकि ये अंक आम तौर पर वैज्ञानिक संकेतन में संग्रहीत होते हैं। इसलिए, उदाहरण के लिए, 1/10 को संग्रहीत करने के बजाय 0.0001100हम इसे कुछ के रूप में संग्रहीत कर सकते हैं1.10011 * 2^-4 , जो इस बात पर निर्भर करता है कि हमने कितने घटक और मंटिसा के लिए आवंटित किया है। यह प्रभावित करता है कि आप अपनी गणना के लिए सटीक कितने अंक प्राप्त करते हैं।

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


4

पायथन 3.5 के बाद से आप math.isclose()अनुमानित समानता के परीक्षण के लिए फ़ंक्शन का उपयोग कर सकते हैं :

>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
>>> 0.1 + 0.2 == 0.3
False

3

चूंकि यह थ्रेड वर्तमान फ्लोटिंग पॉइंट कार्यान्वयन पर एक सामान्य चर्चा में थोड़ा आगे बढ़ गया है, इसलिए मुझे लगता है कि उनके मुद्दों को ठीक करने पर परियोजनाएं हैं।

उदाहरण के लिए, https://posithub.org/ पर एक नज़र डालें , जो पॉज़िट (और इसके पूर्ववर्ती यूम) नामक एक संख्या प्रकार को दर्शाता है जो कम बिट्स के साथ बेहतर सटीकता की पेशकश करने का वादा करता है। अगर मेरी समझ सही है, तो यह सवाल में आने वाली समस्याओं को भी ठीक करता है। काफी दिलचस्प परियोजना, इसके पीछे का व्यक्ति एक गणितज्ञ है यह डॉ। जॉन गुस्ताफसन है । पूरी बात खुला स्रोत है, सी / सी ++, पायथन, जूलिया और सी # ( https://hastlayer.com/arithmetics ) में कई वास्तविक कार्यान्वयन के साथ ।


3

यह वास्तव में बहुत सरल है। जब आपके पास आधार 10 प्रणाली (हमारी तरह) है, तो यह केवल उन अंशों को व्यक्त कर सकता है जो आधार के प्रमुख कारक का उपयोग करते हैं। 10 के मुख्य कारक 2 हैं और 5. तो 1/2, 1/4, 1/5, 1/8, और 1/10 सभी को साफ-साफ व्यक्त किया जा सकता है क्योंकि हर के सभी 10. के प्रमुख कारकों का उपयोग करते हैं। इसके विपरीत, 1 / 3, 1/6 और 1/7 सभी दशमलव को दोहरा रहे हैं क्योंकि उनके भाजक 3 या 7. के प्रमुख कारक का उपयोग करते हैं। द्विआधारी (या आधार 2) में, एकमात्र मुख्य कारक 2 है। इसलिए आप केवल साफ-सुथरा अंश ही व्यक्त कर सकते हैं केवल एक मुख्य कारक के रूप में 2 होते हैं। बाइनरी में, 1/2, 1/4, 1/8 सभी को दशमलव के रूप में स्पष्ट रूप से व्यक्त किया जाएगा। जबकि, 1/5 या 1/10 दशमलव दोहरा रहा होगा। इसलिए 0.1 और 0.2 (1/10 और 1/5) जबकि बेस 10 सिस्टम में साफ दशमलव, बेस 2 सिस्टम में कंप्यूटर को संचालित कर रहे हैं, जब आप इन दोहराए जाने वाले दशमलव पर गणित करते हैं।

से https://0.30000000000000004.com/


3

जैसे दशमलव संख्या 0.1, 0.2, और 0.3बाइनरी में ठीक वैसे ही प्रदर्शित नहीं कर रहे हैं चल बिन्दु प्रकार इनकोडिंग। के लिए अनुमानों की राशि 0.1और 0.2के लिए इस्तेमाल किया सन्निकटन से भिन्न 0.3है, इसलिए का झूठ 0.1 + 0.2 == 0.3के रूप में अधिक स्पष्ट रूप से यहाँ देखा जा सकता:

#include <stdio.h>

int main() {
    printf("0.1 + 0.2 == 0.3 is %s\n", 0.1 + 0.2 == 0.3 ? "true" : "false");
    printf("0.1 is %.23f\n", 0.1);
    printf("0.2 is %.23f\n", 0.2);
    printf("0.1 + 0.2 is %.23f\n", 0.1 + 0.2);
    printf("0.3 is %.23f\n", 0.3);
    printf("0.3 - (0.1 + 0.2) is %g\n", 0.3 - (0.1 + 0.2));
    return 0;
}

आउटपुट:

0.1 + 0.2 == 0.3 is false
0.1 is 0.10000000000000000555112
0.2 is 0.20000000000000001110223
0.1 + 0.2 is 0.30000000000000004440892
0.3 is 0.29999999999999998889777
0.3 - (0.1 + 0.2) is -5.55112e-17

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

_Decimal32, _Decimal64और _Decimal128प्रकार आपके सिस्टम पर उपलब्ध हो सकता है (उदाहरण के लिए, जीसीसी उन पर समर्थन करता है, चयनित लक्ष्य है, लेकिन बजना उन पर समर्थन नहीं करता है ओएस एक्स )।


1

Math.sum (जावास्क्रिप्ट) .... ऑपरेटर प्रतिस्थापन की तरह

.1 + .0001 + -.1 --> 0.00010000000000000286
Math.sum(.1 , .0001, -.1) --> 0.0001

Object.defineProperties(Math, {
    sign: {
        value: function (x) {
            return x ? x < 0 ? -1 : 1 : 0;
            }
        },
    precision: {
        value: function (value, precision, type) {
            var v = parseFloat(value), 
                p = Math.max(precision, 0) || 0, 
                t = type || 'round';
            return (Math[t](v * Math.pow(10, p)) / Math.pow(10, p)).toFixed(p);
        }
    },
    scientific_to_num: {  // this is from https://gist.github.com/jiggzson
        value: function (num) {
            //if the number is in scientific notation remove it
            if (/e/i.test(num)) {
                var zero = '0',
                        parts = String(num).toLowerCase().split('e'), //split into coeff and exponent
                        e = parts.pop(), //store the exponential part
                        l = Math.abs(e), //get the number of zeros
                        sign = e / l,
                        coeff_array = parts[0].split('.');
                if (sign === -1) {
                    num = zero + '.' + new Array(l).join(zero) + coeff_array.join('');
                } else {
                    var dec = coeff_array[1];
                    if (dec)
                        l = l - dec.length;
                    num = coeff_array.join('') + new Array(l + 1).join(zero);
                }
            }
            return num;
         }
     }
    get_precision: {
        value: function (number) {
            var arr = Math.scientific_to_num((number + "")).split(".");
            return arr[1] ? arr[1].length : 0;
        }
    },
    sum: {
        value: function () {
            var prec = 0, sum = 0;
            for (var i = 0; i < arguments.length; i++) {
                prec = this.max(prec, this.get_precision(arguments[i]));
                sum += +arguments[i]; // force float to convert strings to number
            }
            return Math.precision(sum, prec);
        }
    }
});

फ्लोट त्रुटियों से बचने के लिए ऑपरेटरों के बजाय मठ का उपयोग करने का विचार है

Math.sum ऑटो सटीक का उपयोग करने का पता लगाता है

Math.sum किसी भी तर्क को स्वीकार करता है


1
मुझे यकीन नहीं है कि आपने इस सवाल का जवाब दिया है, " ये गलतियाँ क्यों होती हैं? "
वाई हा ली

एक तरह से आप सही हैं, लेकिन मैं इस मुद्दे के बारे में एक जावास्क्रिप्ट अजीब व्यवहार से यहाँ आया था ... मैं सिर्फ इस तरह का समाधान साझा करना चाहता
हूं

0

मैंने इस दिलचस्प मुद्दे को फ्लोटिंग पॉइंट्स के आसपास देखा:

निम्नलिखित परिणामों पर विचार करें:

error = (2**53+1) - int(float(2**53+1))
>>> (2**53+1) - int(float(2**53+1))
1

हम स्पष्ट रूप से एक विराम बिंदु देख सकते हैं 2**53+1- जब तक सब ठीक काम करता है 2**53

>>> (2**53) - int(float(2**53))
0

यहां छवि विवरण दर्ज करें

डबल-परिशुद्धता बाइनरी के कारण ऐसा होता है: IEEE 754 डबल-सटीक बाइनरी-फ़्लोटिंग फ़्लोटिंग-पॉइंट स्वरूप: बाइनरी 64

डबल-सटीक फ़्लोटिंग-पॉइंट प्रारूप के लिए विकिपीडिया पृष्ठ से :

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

  • साइन बिट: 1 बिट
  • घातांक: 11 बिट्स
  • महत्वपूर्ण सटीकता: 53 बिट्स (52 स्पष्ट रूप से संग्रहीत)

यहां छवि विवरण दर्ज करें

किसी दिए गए पक्षपाती प्रतिपादक के साथ एक 64-बिट डबल-सटीक डेटम द्वारा ग्रहण किया गया वास्तविक मान और 52-बिट अंश है

यहां छवि विवरण दर्ज करें

या

यहां छवि विवरण दर्ज करें

मेरी ओर इशारा करने के लिए @a_guest का धन्यवाद।


-1

एक अलग प्रश्न को इस डुप्लिकेट के रूप में नामित किया गया है:

C ++ में, cout << xकिसी डिबगर के लिए दिखाए जाने वाले मान से भिन्न का परिणाम क्यों होता है x?

xसवाल में एक है floatचर।

एक उदाहरण होगा

float x = 9.9F;

डिबगर दिखाता है 9.89999962, coutऑपरेशन का आउटपुट है 9.9

इसका उत्तर यह है कि 6 के coutलिए डिफ़ॉल्ट परिशुद्धता floatहै, इसलिए यह 6 दशमलव अंकों के लिए गोल है।

संदर्भ के लिए यहां देखें


1
IMO - इसे यहां पोस्ट करना गलत दृष्टिकोण था। मुझे पता है कि यह निराशाजनक है, लेकिन जिन लोगों को मूल प्रश्न के उत्तर की आवश्यकता है (जाहिरा तौर पर अब हटाए गए!) उन्हें यहां नहीं मिलेगा। यदि आप वास्तव में महसूस करते हैं कि आपका काम बचाना चाहता है, तो मैं सुझाव दूंगा: 1) दूसरे प्रश्न की तलाश में है जो वास्तव में उत्तर देता है, 2) एक स्व-उत्तर वाला प्रश्न बनाना।
स्टीफन सी।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.