निम्नलिखित कोड पर विचार करें:
0.1 + 0.2 == 0.3 -> false
0.1 + 0.2 -> 0.30000000000000004
ये अशुद्धि क्यों होती हैं?
निम्नलिखित कोड पर विचार करें:
0.1 + 0.2 == 0.3 -> false
0.1 + 0.2 -> 0.30000000000000004
ये अशुद्धि क्यों होती हैं?
जवाबों:
बाइनरी फ्लोटिंग पॉइंट मैथ इस तरह है। अधिकांश प्रोग्रामिंग भाषाओं में, यह 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-4
C99 हेक्सफ़्लोट संकेतन के एक एनालॉग में, जहां ...
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
अपने विशेष आवेदन के लिए चुने जाने की आवश्यकता है - और यह आपके पास कितना "विगले कमरा" है, जिसकी आप अनुमति देने के लिए तैयार हैं, और आपकी तुलना करने वाली सबसे बड़ी संख्या क्या हो सकती है (सटीक मुद्दों के नुकसान के कारण) )। अपनी पसंद की भाषा में "एप्सिलॉन" स्टाइल कांस्टेंट से सावधान रहें। इनका उपयोग सहिष्णुता मूल्यों के रूप में नहीं किया जाना है।
मेरा मानना है कि मुझे एक हार्डवेयर डिज़ाइनर का दृष्टिकोण जोड़ना चाहिए क्योंकि मैं फ़्लोटिंग हार्डवेयर का डिज़ाइन और निर्माण करता हूँ। त्रुटि की उत्पत्ति को जानने से यह समझने में मदद मिल सकती है कि सॉफ्टवेयर में क्या हो रहा है, और आखिरकार, मुझे आशा है कि इससे फ्लोटिंग पॉइंट त्रुटियां होने के कारणों की व्याख्या करने में मदद मिलती है और समय के साथ जमा होने लगता है।
इंजीनियरिंग के दृष्टिकोण से, अधिकांश फ्लोटिंग पॉइंट ऑपरेशंस में कुछ तत्व की त्रुटि होगी क्योंकि हार्डवेयर जो फ्लोटिंग पॉइंट कंप्यूटेशन करता है, उसे केवल अंतिम स्थान पर एक यूनिट के आधे से कम की त्रुटि की आवश्यकता होती है। इसलिए, बहुत से हार्डवेयर सटीक रूप से बंद हो जाएंगे, जो कि एक ही ऑपरेशन के लिए अंतिम स्थान पर एक इकाई के आधे से भी कम की त्रुटि उत्पन्न करने के लिए आवश्यक है जो कि फ्लोटिंग पॉइंट डिवीजन में विशेष रूप से समस्याग्रस्त है। एक एकल ऑपरेशन का गठन इस बात पर निर्भर करता है कि यूनिट कितने ऑपरेंड लेता है। अधिकांश के लिए, यह दो है, लेकिन कुछ इकाइयां 3 या अधिक ऑपरेंड लेती हैं। इस वजह से, इस बात की कोई गारंटी नहीं है कि बार-बार किए गए ऑपरेशन के परिणामस्वरूप एक वांछनीय त्रुटि होगी क्योंकि त्रुटियों को समय के साथ जोड़ा जाता है।
अधिकांश प्रोसेसर IEEE-754 मानक का पालन करते हैं , लेकिन कुछ का उपयोग असामान्य, या विभिन्न मानकों पर किया जाता है। उदाहरण के लिए, IEEE-754 में एक अपभ्रंश मोड है जो परिशुद्धता की कीमत पर बहुत छोटे फ्लोटिंग पॉइंट नंबरों के प्रतिनिधित्व की अनुमति देता है। हालाँकि, निम्नलिखित IEEE-754 के सामान्यीकृत मोड को कवर करेगा, जो ऑपरेशन का विशिष्ट मोड है।
IEEE-754 मानक में, हार्डवेयर डिजाइनरों को त्रुटि / एप्सिलॉन के किसी भी मूल्य की अनुमति दी जाती है, जब तक कि यह अंतिम स्थान में एक इकाई के आधे से कम हो, और परिणाम अंतिम में एक इकाई के आधे से भी कम हो। एक ऑपरेशन के लिए जगह। यह बताता है कि जब दोहराए जाने वाले संचालन क्यों होते हैं, तो त्रुटियां बढ़ जाती हैं। IEEE-754 दोहरी सटीकता के लिए, यह 54 वीं बिट है, क्योंकि 53 बिट्स का उपयोग संख्यात्मक भाग (सामान्यीकृत) का प्रतिनिधित्व करने के लिए किया जाता है, जिसे फ्लोटिंग पॉइंट संख्या (जैसे 5.3e5 में 5.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 डिवीजन राउंडिंग त्रुटि: प्राप्तकर्ता का अनुमोदन
भागफल चयन तालिका में कौन-से पारस्परिक गुण हैं, यह विभाजन विधि पर निर्भर करता है : एसआरटी डिवीजन जैसे धीमी डिवीजन, या फास्ट डिवीजन जैसे गोल्डस्किमिड डिवीजन; प्रत्येक प्रविष्टि को न्यूनतम संभव त्रुटि प्राप्त करने के प्रयास में डिवीजन एल्गोरिथ्म के अनुसार संशोधित किया गया है। किसी भी मामले में, हालांकि, सभी पारस्परिक सन्निकटन हैंवास्तविक पारस्परिक और त्रुटि के कुछ तत्व का परिचय। धीमी गति से विभाजन और तेजी से विभाजन दोनों तरीके भागफल की गणना करते हैं, यानी भागफल की कुछ संख्याओं की गणना प्रत्येक चरण में की जाती है, फिर परिणाम को लाभांश से घटाया जाता है, और विभाजक चरणों को दोहराता है जब तक कि त्रुटि एक के आधे से कम न हो जाए अंतिम स्थान पर इकाई। स्लो डिवीजन विधियाँ प्रत्येक चरण में भागफल के निश्चित अंकों की गणना करती हैं और आमतौर पर बनाने के लिए कम खर्चीली होती हैं, और तेज़ डिवीज़न विधियाँ प्रति चरण की एक परिवर्तनीय संख्या की गणना करती हैं और आमतौर पर बनाने के लिए अधिक महंगी होती हैं। विभाजन विधियों का सबसे महत्वपूर्ण हिस्सा यह है कि उनमें से अधिकांश एक पारस्परिक सन्निकटन के द्वारा बार-बार गुणा पर भरोसा करते हैं , इसलिए वे त्रुटि से ग्रस्त हैं।
सभी ऑपरेशनों में गोलाई त्रुटियों का एक अन्य कारण IEEE-754 की अनुमति देने वाले अंतिम उत्तर के छंटनी के विभिन्न तरीके हैं। ट्रंकेट, राउंड-प्रति-शून्य, राउंड-टू-निकटतम (डिफ़ॉल्ट), राउंड-डाउन और राउंड-अप है। सभी विधियाँ एक एकल ऑपरेशन के लिए अंतिम स्थान पर एक इकाई से कम की त्रुटि का एक तत्व पेश करती हैं। समय के साथ और बार-बार होने वाले ऑपरेशन में, ट्रंकेशन परिणामी त्रुटि के लिए संचयी रूप से जोड़ता है। यह ट्रंकेशन त्रुटि विशेष रूप से घातांक में समस्याग्रस्त है, जिसमें दोहराया गुणन के कुछ रूप शामिल हैं।
चूँकि हार्डवेयर जो फ्लोटिंग पॉइंट गणना करता है, उसे केवल एक ही ऑपरेशन के लिए अंतिम स्थान पर एक यूनिट के आधे से भी कम की त्रुटि के साथ परिणाम प्राप्त करने की आवश्यकता होती है, यदि नहीं देखा गया तो त्रुटि बार-बार होने वाले ऑपरेशन से अधिक हो जाएगी। यही कारण है कि कम्प्यूटेशंस में, एक बाध्य त्रुटि की आवश्यकता होती है, गणितज्ञ तरीकों का उपयोग करते हैं जैसे कि IEEE-754 के अंतिम स्थान पर गोल-से-निकटतम अंकों का उपयोग करना , क्योंकि, समय के साथ, त्रुटियों को एक दूसरे को रद्द करने की अधिक संभावना है बाहर, और अंतराल अंकगणित IEEE 754 गोलाई मोड की विविधताओं के साथ संयुक्तगोलाई की त्रुटियों की भविष्यवाणी करना और उन्हें ठीक करना। अन्य राउंडिंग मोड्स की तुलना में इसकी कम सापेक्ष त्रुटि के कारण, राउंड निकटतम निकटतम अंक (अंतिम स्थान पर), IEEE-754 का डिफ़ॉल्ट राउंडिंग मोड है।
ध्यान दें कि अंतिम स्थान पर डिफ़ॉल्ट राउंडिंग मोड, राउंड-टू-निकटतम सम अंक , एक ऑपरेशन के लिए अंतिम स्थान में एक इकाई के आधे से भी कम की त्रुटि की गारंटी देता है। ट्रंकेशन, राउंड-अप और अकेले राउंड डाउन का उपयोग करने से एक त्रुटि हो सकती है जो अंतिम स्थान में एक इकाई के आधे से अधिक है, लेकिन अंतिम स्थान में एक इकाई से कम है, इसलिए जब तक वे नहीं होते हैं तब तक इन विधियों की सिफारिश नहीं की जाती है। अंतराल अंकगणित में इस्तेमाल किया।
संक्षेप में, फ्लोटिंग पॉइंट ऑपरेशंस में त्रुटियों का मूल कारण हार्डवेयर में ट्रंकेशन का एक संयोजन है, और विभाजन के मामले में एक पारस्परिक ट्रंकेशन है। चूंकि IEEE-754 मानक को केवल एकल ऑपरेशन के लिए अंतिम स्थान में एक इकाई के आधे से भी कम की त्रुटि की आवश्यकता होती है, इसलिए दोहराए गए संचालन पर फ़्लोटिंग पॉइंट त्रुटियां तब तक जोड़ देंगी जब तक कि इसे सही नहीं किया जाता है।
जब आप .1 या 1/10 को बेस 2 (बाइनरी) में परिवर्तित करते हैं, तो आपको दशमलव बिंदु के बाद एक दोहराव वाला पैटर्न मिलता है, ठीक उसी तरह जैसे आधार 10 में 1/3 का प्रतिनिधित्व करने की कोशिश करना। मूल्य सटीक नहीं है, और इसलिए आप ऐसा नहीं कर सकते सामान्य फ्लोटिंग पॉइंट विधियों का उपयोग करके इसके साथ सटीक गणित।
यहाँ अधिकांश उत्तर इस प्रश्न को बहुत शुष्क, तकनीकी शब्दों में संबोधित करते हैं। मैं इसे इस संदर्भ में संबोधित करना चाहूंगा कि सामान्य मनुष्य समझ सकता है।
कल्पना करें कि आप पिज्जा को टुकड़ा करने की कोशिश कर रहे हैं। आपके पास एक रोबोट पिज्जा कटर है जो पिज्जा स्लाइस को बिल्कुल आधे में काट सकता है । यह एक पूरे पिज्जा को आधा कर सकता है, या यह एक मौजूदा स्लाइस को आधा कर सकता है, लेकिन किसी भी मामले में, हॉल्टिंग हमेशा सटीक होती है।
उस पिज्जा कटर में बहुत महीन हलचल होती है, और यदि आप पूरे पिज्जा के साथ शुरू करते हैं, तो उसे आधा कर दें, और हर बार सबसे छोटे स्लाइस को रोकते रहें, आप स्लाइस को 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 कुछ प्रोग्रामिंग भाषाएं पिज्जा कटर भी प्रदान करती हैं जो स्लाइस को सटीक दसियों में विभाजित कर सकती हैं । हालांकि ऐसे पिज्जा कटर असामान्य हैं, यदि आपके पास एक तक पहुंच है, तो आपको इसका उपयोग तब करना चाहिए जब यह महत्वपूर्ण हो कि एक-दसवां या एक-पांचवां टुकड़ा प्राप्त करने में सक्षम हो।
फ्लोटिंग पॉइंट राउंडिंग एरर। 0.1 को बेस -2 में सही-सही दर्शाया नहीं जा सकता है क्योंकि बेस -10 में 5 का मुख्य कारक है। ठीक 1/3 के रूप में दशमलव में प्रतिनिधित्व करने के लिए अंकों की एक अनंत संख्या लगती है, लेकिन बेस -3 में "0.1" है, 0.1 आधार -2 में अनंत संख्या में अंक लेता है जहां यह आधार -10 में नहीं होता है। और कंप्यूटर में अनंत मात्रा में मेमोरी नहीं होती है।
अन्य सही उत्तरों के अलावा, आप फ्लोटिंग-पॉइंट अंकगणित की समस्याओं से बचने के लिए अपने मूल्यों को बढ़ाने पर विचार कर सकते हैं।
उदाहरण के लिए:
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) ।
मेरा उत्तर काफी लंबा है, इसलिए मैंने इसे तीन खंडों में विभाजित किया है। चूंकि सवाल फ्लोटिंग पॉइंट गणित के बारे में है, इसलिए मैंने इस बात पर जोर दिया है कि मशीन वास्तव में क्या करती है। मैंने इसे डबल (64 बिट) सटीक करने के लिए विशिष्ट भी बनाया है, लेकिन तर्क किसी भी फ्लोटिंग पॉइंट अंकगणित पर समान रूप से लागू होता है।
प्रस्तावना
एक आईईईई 754 डबल परिशुद्धता द्विआधारी फ्लोटिंग प्वाइंट प्रारूप (binary64) नंबर फार्म की एक संख्या का प्रतिनिधित्व
मान = (-1) ^ s * (1.m 51 m 50 ... m 2 m 1 m 0 ) 2 * 2 e-1023
64 बिट्स में:
1
यदि संख्या नकारात्मक है, 0
अन्यथा 1 ।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.2
IEEE 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
अंतिम कुछ बिट्स गोल कर रहे हैं:।
कंप्यूटर में संग्रहीत फ़्लोटिंग पॉइंट संख्या में दो भाग होते हैं, एक पूर्णांक और एक घातांक जिसे आधार को पूर्णांक भाग से गुणा और गुणा किया जाता है।
यदि कंप्यूटर बेस 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
। फ्लोटिंग पॉइंट नंबर आमतौर पर प्रदर्शन के लिए गोल होते हैं।
फ्लोटिंग पॉइंट राउंडिंग एरर। से क्या हर कंप्यूटर वैज्ञानिक चाहिए नो फ्लोटिंग प्वाइंट अंकगणित के बारे में :
बिट्स की एक सीमित संख्या में असीम रूप से कई वास्तविक संख्याओं को निचोड़ने के लिए एक अनुमानित प्रतिनिधित्व की आवश्यकता होती है। यद्यपि असीम रूप से कई पूर्णांक हैं, अधिकांश कार्यक्रमों में पूर्णांक संगणनाओं का परिणाम 32 बिट्स में संग्रहीत किया जा सकता है। इसके विपरीत, बिट्स की किसी भी निश्चित संख्या को देखते हुए, वास्तविक संख्याओं के साथ अधिकांश गणना उन मात्राओं का उत्पादन करेगी जो कि कई बिट्स का उपयोग करके बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है। इसलिए फ्लोटिंग-पॉइंट गणना का परिणाम अक्सर अपने परिमित प्रतिनिधित्व में वापस फिट होने के लिए गोल होना चाहिए। यह गोल त्रुटि अस्थायी-बिंदु संगणना की विशेषता है।
बहुत सारे अच्छे उत्तर पोस्ट किए गए हैं, लेकिन मैं एक और अपील करना चाहूंगा।
सभी संख्याओं को फ़्लोट्स / डबल्स के माध्यम से नहीं दिखाया जा सकता है। उदाहरण के लिए, संख्या "0.2" को IEEE754 फ्लोट पॉइंट मानक में एकल परिशुद्धता में "0.200000003" के रूप में दर्शाया जाएगा।
हुड के तहत वास्तविक संख्या की दुकान के लिए मॉडल के रूप में फ्लोट संख्या का प्रतिनिधित्व करते हैं
भले ही आप 0.2
आसानी से टाइप कर सकते हैं, FLT_RADIX
और DBL_RADIX
2 है; एफपीयू वाले कंप्यूटर के लिए 10 नहीं जो "बाइनरी फ्लोटिंग-पॉइंट अंकगणित (आईएसओ / आईईईई स्टैड 754-1985) के लिए आईईईई मानक" का उपयोग करता है।
इसलिए ऐसी संख्याओं का सही प्रतिनिधित्व करना थोड़ा कठिन है। यहां तक कि अगर आप इस चर को स्पष्ट रूप से किसी भी मध्यवर्ती गणना के बिना निर्दिष्ट करते हैं।
इस प्रसिद्ध डबल सटीक प्रश्न से संबंधित कुछ आंकड़े।
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%)।
सारांश
चल बिन्दु गणित है सटीक, दुर्भाग्य से, यह अच्छी तरह से हमारी सामान्य आधार -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-बिट स्टोरेज के लिए व्यक्तिगत चुंबकीय कोर , लेकिन यह एक और कहानी है। )
निष्कर्ष
यदि आप किसी बैंक में सेम की गिनती कर रहे हैं, तो सॉफ्टवेयर सॉल्यूशंस जो पहले स्थान पर दशमलव स्ट्रिंग अभ्यावेदन का उपयोग करते हैं, पूरी तरह से अच्छी तरह से काम करते हैं। लेकिन आप इस तरह से क्वांटम क्रोमोडायनामिक्स या एरोडायनामिक्स नहीं कर सकते।
nextafter()
IEEE फ़्लोट के बाइनरी प्रतिनिधित्व पर पूर्णांक वृद्धि या गिरावट के साथ लागू कर सकते हैं । इसके अलावा, आप फ़्लोटर्स की तुलना पूर्णांक के रूप में कर सकते हैं और सही उत्तर प्राप्त कर सकते हैं, जब वे नकारात्मक हों (साइन-परिमाण बनाम 2 के पूरक के कारण)।
क्या आपने डक्ट टेप समाधान की कोशिश की?
यह निर्धारित करने का प्रयास करें कि त्रुटियां कब होती हैं और उन्हें कम करके ठीक करें यदि कथन, तो यह सुंदर नहीं है, लेकिन कुछ समस्याओं के लिए यह एकमात्र समाधान है और यह उनमें से एक है।
if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
else { return n * 0.1 + 0.000000000000001 ;}
मुझे सी # में एक वैज्ञानिक सिमुलेशन परियोजना में एक ही समस्या थी, और मैं आपको बता सकता हूं कि यदि आप तितली के प्रभाव को अनदेखा करते हैं, तो यह एक बड़े वसा वाले ड्रैगन की ओर जाता है और आपको एक ** में काटता है।
सबसे अच्छा समाधान प्रदान करने के लिए मैं कह सकता हूं कि मैंने निम्नलिखित विधि की खोज की है:
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
यह बिल्कुल समान है, हालांकि काम नहीं करेगा! मैं पहला समाधान पसंद करता हूं क्योंकि मैं इसे एक फ़ंक्शन के रूप में लागू कर सकता हूं जो इनपुट फ्लोट को सटीक आउटपुट फ्लोट में परिवर्तित करता है।
वे अजीब संख्याएँ दिखाई देती हैं क्योंकि कंप्यूटर बाइनरी (बेस 2) नंबर सिस्टम का उपयोग गणना उद्देश्यों के लिए करते हैं, जबकि हम दशमलव (बेस 10) का उपयोग करते हैं।
बहुसंख्यक भिन्न संख्याएँ हैं जिनका बाइनरी या दशमलव या दोनों में सटीक प्रतिनिधित्व नहीं किया जा सकता है। परिणाम - एक गोल (लेकिन सटीक) संख्या परिणाम।
इस प्रश्न के कई डुप्लिकेट विशिष्ट संख्याओं पर फ़्लोटिंग पॉइंट राउंडिंग के प्रभावों के बारे में पूछते हैं। व्यवहार में, इसके बारे में सिर्फ पढ़ने के बजाय ब्याज की गणना के सटीक परिणामों को देखकर यह महसूस करना आसान है कि यह कैसे काम करता है। इस तरह के एक परिवर्तित रूप में - कुछ भाषाओं में है कि कुछ करने के तरीके प्रदान करते हैं 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 है, जो एक सम अंक में समाप्त होता है और इसलिए सही परिणाम है।
यह देखते हुए कि किसी ने भी इसका उल्लेख नहीं किया है ...
कुछ उच्च स्तरीय भाषाएं जैसे पायथन और जावा बाइनरी फ्लोटिंग पॉइंट सीमाओं को दूर करने के लिए टूल के साथ आते हैं। उदाहरण के लिए:
पायथन के 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)
जोड़े के रूप में तर्कसंगत संख्याओं का प्रतिनिधित्व करते हैं और वे दशमलव फ़्लोटिंग पॉइंट अंकगणित की तुलना में अधिक सटीक परिणाम दे सकते हैं।
इन समाधानों में से कोई भी सही नहीं है (खासकर अगर हम प्रदर्शनों को देखते हैं, या अगर हमें बहुत उच्च परिशुद्धता की आवश्यकता होती है), लेकिन फिर भी वे बाइनरी फ्लोटिंग पॉइंट अंकगणित के साथ बड़ी संख्या में समस्याओं को हल करते हैं।
क्या मैं सिर्फ जोड़ सकता हूं; लोग हमेशा इसे कंप्यूटर की समस्या मानते हैं, लेकिन अगर आप अपने हाथों (आधार 10) से गिनते हैं, तो आप (1/3+1/3=2/3)=true
तब तक नहीं मिल सकते जब तक कि आपके पास 0.333 ... 0.333 ... जोड़ने की अनंतता न हो ... बस (1/10+2/10)!==3/10
आधार में समस्या के साथ २, आप इसे ०.३३३ + ०.३३३ = ०.६६६ पर छांटते हैं और शायद इसे ०.६६ which पर गोल करते हैं जो तकनीकी रूप से गलत भी होगा।
टर्नरी में गिनती, और तिहाई हालांकि एक समस्या नहीं है - शायद प्रत्येक हाथ पर 15 उंगलियों के साथ कुछ दौड़ पूछेंगे कि आपका दशमलव गणित क्यों टूट गया था ...
जिस प्रकार का फ्लोटिंग-पॉइंट गणित डिजिटल कंप्यूटर में लागू किया जा सकता है, वह आवश्यक रूप से उन पर वास्तविक संख्याओं और परिचालनों के सन्निकटन का उपयोग करता है। ( मानक संस्करण प्रलेखन के पचास से अधिक पृष्ठों तक चलता है और इसकी इरेटा और आगे शोधन से निपटने के लिए एक समिति है।)
यह सन्निकटन विभिन्न प्रकार के सन्निकटन का मिश्रण है, जिनमें से प्रत्येक को सटीक रूप से विचलन के अपने विशिष्ट तरीके के कारण अनदेखा या ध्यान से देखा जा सकता है। इसमें हार्डवेयर और सॉफ्टवेयर दोनों स्तरों पर कई स्पष्ट असाधारण मामले शामिल हैं जो अधिकांश लोग नोटिस नहीं करने का दिखावा करते हुए सही अतीत पर चलते हैं।
यदि आपको अनंत सटीकता की आवश्यकता है (उदाहरण के लिए, संख्या, इसके कई छोटे स्टैंड-इन में से एक के बजाय), तो आपको इसके बजाय एक प्रतीकात्मक गणित कार्यक्रम लिखना या उपयोग करना चाहिए।
लेकिन अगर आप इस विचार के साथ ठीक हैं कि कभी-कभी फ़्लोटिंग-पॉइंट गणित मूल्य में फ़ज़ी होता है और तर्क और त्रुटियां जल्दी से जमा हो सकती हैं, और आप उस के लिए अनुमति देने के लिए अपनी आवश्यकताओं और परीक्षणों को लिख सकते हैं, तो आपका कोड अक्सर आपके साथ मिल सकता है। आपका FPU
बस मज़े के लिए, मैंने मानक 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 है।
इसे देखने का एक और तरीका: संख्याओं का प्रतिनिधित्व करने के लिए प्रयुक्त 64 बिट्स हैं। परिणाम के रूप में 2 से अधिक कोई रास्ता नहीं है ** 64 = 18,446,744,073,709,551,616 विभिन्न संख्याओं का सटीक प्रतिनिधित्व किया जा सकता है।
हालांकि, मैथ का कहना है कि पहले से ही 0 और 1. के बीच कई डेसीमल पहले से ही हैं, IEE 754 इनको 64 बिट्स का उपयोग करने के लिए एक एन्कोडिंग को परिभाषित करता है, जो कि अधिक बड़ी संख्या वाले स्थान और NaN और + / - इन्फिनिटी के लिए कुशलतापूर्वक उपयोग करता है, इसलिए सही प्रतिनिधित्व के साथ अंतराल के बीच अंतराल हैं। संख्या केवल अनुमानित है।
दुर्भाग्य से 0.3 अंतर में बैठता है।
आधार के साथ बेस दस में काम करने की कल्पना करें, कहते हैं, सटीकता के 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
, जो इस बात पर निर्भर करता है कि हमने कितने घटक और मंटिसा के लिए आवंटित किया है। यह प्रभावित करता है कि आप अपनी गणना के लिए सटीक कितने अंक प्राप्त करते हैं।
उतावलापन यह है कि इन गोल त्रुटियों के कारण आप अनिवार्य रूप से फ्लोटिंग-पॉइंट नंबरों पर == का उपयोग नहीं करना चाहते हैं। इसके बजाय, आप जांच सकते हैं कि उनके अंतर का पूर्ण मान कुछ निश्चित छोटी संख्या से छोटा है या नहीं।
पायथन 3.5 के बाद से आप math.isclose()
अनुमानित समानता के परीक्षण के लिए फ़ंक्शन का उपयोग कर सकते हैं :
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
>>> 0.1 + 0.2 == 0.3
False
चूंकि यह थ्रेड वर्तमान फ्लोटिंग पॉइंट कार्यान्वयन पर एक सामान्य चर्चा में थोड़ा आगे बढ़ गया है, इसलिए मुझे लगता है कि उनके मुद्दों को ठीक करने पर परियोजनाएं हैं।
उदाहरण के लिए, https://posithub.org/ पर एक नज़र डालें , जो पॉज़िट (और इसके पूर्ववर्ती यूम) नामक एक संख्या प्रकार को दर्शाता है जो कम बिट्स के साथ बेहतर सटीकता की पेशकश करने का वादा करता है। अगर मेरी समझ सही है, तो यह सवाल में आने वाली समस्याओं को भी ठीक करता है। काफी दिलचस्प परियोजना, इसके पीछे का व्यक्ति एक गणितज्ञ है यह डॉ। जॉन गुस्ताफसन है । पूरी बात खुला स्रोत है, सी / सी ++, पायथन, जूलिया और सी # ( https://hastlayer.com/arithmetics ) में कई वास्तविक कार्यान्वयन के साथ ।
यह वास्तव में बहुत सरल है। जब आपके पास आधार 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 सिस्टम में कंप्यूटर को संचालित कर रहे हैं, जब आप इन दोहराए जाने वाले दशमलव पर गणित करते हैं।
जैसे दशमलव संख्या 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
प्रकार आपके सिस्टम पर उपलब्ध हो सकता है (उदाहरण के लिए, जीसीसी उन पर समर्थन करता है, चयनित लक्ष्य है, लेकिन बजना उन पर समर्थन नहीं करता है ओएस एक्स )।
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 किसी भी तर्क को स्वीकार करता है
निम्नलिखित परिणामों पर विचार करें:
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 का धन्यवाद।
एक अलग प्रश्न को इस डुप्लिकेट के रूप में नामित किया गया है:
C ++ में, cout << x
किसी डिबगर के लिए दिखाए जाने वाले मान से भिन्न का परिणाम क्यों होता है x
?
x
सवाल में एक है float
चर।
एक उदाहरण होगा
float x = 9.9F;
डिबगर दिखाता है 9.89999962
, cout
ऑपरेशन का आउटपुट है 9.9
।
इसका उत्तर यह है कि 6 के cout
लिए डिफ़ॉल्ट परिशुद्धता float
है, इसलिए यह 6 दशमलव अंकों के लिए गोल है।
संदर्भ के लिए यहां देखें