क्या फ़्लोटिंग पॉइंट गणित टूट गया है?
निम्नलिखित कोड पर विचार करें:
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
अपने विशेष आवेदन के लिए चुने जाने की जरूरत है - और यह बहुत कुछ करना होगा कि आप कितने "विगले रूम" के साथ अनुमति देने के लिए तैयार हैं, और आपकी तुलना में सबसे बड़ी संख्या क्या हो सकती है (सटीक मुद्दों के नुकसान के कारण) ) का है। अपनी पसंद की भाषा में "एप्सिलॉन" स्टाइल कांस्टेंट से सावधान रहें। इनका उपयोग सहिष्णुता मूल्यों के रूप में नहीं किया जाना है।
एक हार्डवेयर डिज़ाइनर का परिप्रेक्ष्य
मेरा मानना है कि मुझे इसके बाद से हार्डवेयर डिज़ाइनर के दृष्टिकोण को जोड़ना चाहिए क्योंकि मैं फ़्लोटिंग हार्डवेयर का डिज़ाइन और निर्माण करता हूँ। त्रुटि की उत्पत्ति को जानने से सॉफ्टवेयर में क्या हो रहा है, यह समझने में मदद मिल सकती है और आखिरकार, मुझे उम्मीद है कि इससे फ्लोटिंग पॉइंट त्रुटियां होने के कारणों की व्याख्या करने में मदद मिलती है और समय के साथ जमा होने लगता है।
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 मानक में केवल एक ही ऑपरेशन के लिए अंतिम स्थान में एक इकाई के आधे से भी कम की त्रुटि की आवश्यकता होती है, इसलिए दोहराए गए संचालन पर फ़्लोटिंग पॉइंट त्रुटियाँ तब तक बढ़ेंगी जब तक कि इसे सही नहीं किया जाता है।
यह ठीक उसी तरह से टूटा है जैसे दशमलव (बेस -10) अंकन टूटा हुआ है, बस आधार -2 के लिए।
समझने के लिए, दशमलव मूल्य के रूप में 1/3 का प्रतिनिधित्व करने के बारे में सोचें। यह वास्तव में करना असंभव है! उसी तरह, 1/10 (दशमलव 0.1) को बेस 2 (बाइनरी) में "दशमलव" मान के रूप में बिल्कुल नहीं दर्शाया जा सकता है; दशमलव बिंदु के बाद एक दोहराव पैटर्न हमेशा के लिए चला जाता है। मान सटीक नहीं है, और इसलिए आप सामान्य फ़्लोटिंग पॉइंट विधियों का उपयोग करके इसके साथ सटीक गणित नहीं कर सकते हैं।
यहाँ अधिकांश उत्तर इस प्रश्न को बहुत शुष्क, तकनीकी शब्दों में संबोधित करते हैं। मैं इसे इस संदर्भ में संबोधित करना चाहूंगा कि सामान्य मनुष्य समझ सकता है।
कल्पना कीजिए कि आप पिज्जा को टुकड़ा करने की कोशिश कर रहे हैं। आपके पास एक रोबोट पिज्जा कटर है जो पिज्जा स्लाइस को बिल्कुल आधे में काट सकता है । यह एक पूरे पिज्जा को आधा कर सकता है, या यह एक मौजूदा स्लाइस को आधा कर सकता है, लेकिन किसी भी मामले में, हॉल्टिंग हमेशा सटीक होती है।
उस पिज्जा कटर में बहुत महीन हलचल होती है, और यदि आप पूरे पिज्जा के साथ शुरू करते हैं, तो उसे आधा कर दें, और हर बार सबसे छोटे स्लाइस को रोकते रहें, आप स्लाइस को 53 बार कर सकते हैं इससे पहले कि स्लाइस बहुत अधिक सटीक क्षमताओं के लिए छोटा हो । उस बिंदु पर, आप अब उस बहुत पतले स्लाइस को आधा नहीं कर सकते हैं, लेकिन इसे या तो शामिल करना चाहिए या इसे बाहर करना चाहिए।
अब, आप सभी स्लाइस को इस तरह से कैसे पीसेंगे जो एक पिज्जा के दसवें (0.1) या एक-पांचवें (0.2) को जोड़ देगा? वास्तव में इसके बारे में सोचो, और इसे काम करने की कोशिश करो। तुम भी एक असली पिज्जा का उपयोग करने की कोशिश कर सकते हैं, अगर आपके पास हाथ में एक पौराणिक परिशुद्धता पिज्जा कटर है। :-)
अधिकांश अनुभवी प्रोग्रामर, निश्चित रूप से, वास्तविक उत्तर जानते हैं, जो यह है कि उन स्लाइसों का उपयोग करके एक सटीक दसवें या पिज्जा के पांचवें हिस्से को एक साथ टुकड़े करने का कोई तरीका नहीं है, चाहे आप उन्हें कितना भी पतला कर लें। आप एक बहुत अच्छा सन्निकटन कर सकते हैं, और यदि आप 0.2 के सन्निकटन के साथ 0.1 के सन्निकटन को जोड़ते हैं, तो आपको 0.3 का बहुत अच्छा सन्निकटन मिलता है, लेकिन यह अभी भी एक सन्निकटन है।
डबल-सटीक संख्याओं के लिए (जो सटीक है जो आपको अपने पिज्जा को 53 बार आधा करने की अनुमति देता है), तुरंत संख्या कम और 0.1 से अधिक 0.099999999999999999973731531532534692276248981884765625 और 0.100000000000055551515151212578282702118838383408408405 है। उत्तरार्द्ध पूर्व की तुलना में 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.20000000000000001110226246251523236166166806906,203/125/ 0.30000000000000000000444092600626166166166166236236/6326/32/6326/6326/6326/6326/05/6/6136&s=2015 है।
PS कुछ प्रोग्रामिंग भाषाएं पिज्जा कटर भी प्रदान करती हैं जो स्लाइस को सटीक दसियों में विभाजित कर सकती हैं । हालांकि इस तरह के पिज्जा कटर असामान्य हैं, यदि आपके पास एक तक पहुंच है, तो आपको इसका उपयोग तब करना चाहिए जब यह महत्वपूर्ण हो कि एक-दसवां या एक-पांचवां टुकड़ा प्राप्त करने में सक्षम हो।
(मूल रूप से Quora पर पोस्ट किया गया है।)
फ्लोटिंग पॉइंट राउंडिंग एरर। 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 । - अगले 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
s में समाहित है) 2 -1023 है। + 1 - 2 -1023 - 51 ( - 2.225 * 10 -308 )।
बाइनरी के लिए एक डबल सटीक संख्या की ओर मुड़ते हुए
कई ऑनलाइन कन्वर्टर्स द्विआधारी सटीक फ्लोटिंग पॉइंट नंबर को बाइनरी में परिवर्तित करने के लिए मौजूद हैं (उदाहरण के लिए बाइनरीकॉनवर्ट डॉट कॉम पर ), लेकिन यहां एक डबल सेंसिटिव नंबर के लिए IEEE 754 प्रतिनिधित्व प्राप्त करने के लिए कुछ नमूना C # कोड है (मैं कॉलन के साथ तीन भागों को अलग करता हूं :
) :
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 बिट्स में संग्रहीत किया जा सकता है। इसके विपरीत, बिट्स की किसी भी निश्चित संख्या को देखते हुए, वास्तविक संख्याओं के साथ अधिकांश गणना उन मात्राओं का उत्पादन करेगी जो कि कई बिट्स का उपयोग करके बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है। इसलिए फ्लोटिंग-पॉइंट गणना का परिणाम अक्सर अपने परिमित प्रतिनिधित्व में वापस फिट होने के लिए गोल होना चाहिए। यह गोल त्रुटि अस्थायी-बिंदु संगणना की विशेषता है।
मेरा समाधान:
function add(a, b, precision) {
var x = Math.pow(10, precision || 2);
return (Math.round(a * x) + Math.round(b * x)) / x;
}
सटीकता उस अंक की संख्या को संदर्भित करती है जिसे आप दशमलव बिंदु के अलावा संरक्षित करना चाहते हैं।
बहुत सारे अच्छे उत्तर पोस्ट किए गए हैं, लेकिन मैं एक और अपील करना चाहूंगा।
सभी संख्याओं को फ़्लोट्स / डबल्स के माध्यम से नहीं दिखाया जा सकता है। उदाहरण के लिए, संख्या "0.2" को IEEE754 फ़्लोट पॉइंट मानक में एकल परिशुद्धता में "0.200000003" के रूप में दर्शाया जाएगा।
हुड के तहत स्टोर वास्तविक संख्या के लिए मॉडल के रूप में फ्लोट संख्या का प्रतिनिधित्व करते हैं
भले ही आप 0.2
आसानी से टाइप कर सकते हैं, FLT_RADIX
और DBL_RADIX
2 है; FPU वाले कंप्यूटर के लिए 10 नहीं, जो "बाइनरी फ्लोटिंग-पॉइंट अरिथमेटिक (आईएसओ / IEEE स्टैड 754-1985) के लिए IEEE स्टैंडर्ड" का उपयोग करता है।
तो ऐसे नंबरों का प्रतिनिधित्व करना थोड़ा कठिन है। यहां तक कि अगर आप इस चर को बिना किसी मध्यवर्ती गणना के स्पष्ट रूप से निर्दिष्ट करते हैं।
इस प्रसिद्ध डबल सटीक प्रश्न से संबंधित कुछ आंकड़े।
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 n शब्द मिलता है , जो है:
ए / 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 प्रारूप में रहें) और फिर तुलना करें। एक विशिष्ट दशमलव अंश की लंबाई तक पहुंच आउटपुट के साथ अधिकांश समस्याओं को हल करती है।
इसके अलावा, वास्तविक संख्या-क्रंचिंग समस्याओं पर (जिन समस्याओं के लिए FP का आविष्कार जल्दी, भयावह रूप से महंगे कंप्यूटरों पर किया गया था) ब्रह्मांड के भौतिक स्थिरांक और अन्य सभी माप केवल एक अपेक्षाकृत कम संख्या में महत्वपूर्ण आंकड़ों के लिए जाने जाते हैं, इसलिए संपूर्ण समस्या स्थान वैसे भी "अक्षम्य" था। एफपी "सटीकता" इस तरह के आवेदन में कोई समस्या नहीं है।
पूरा मुद्दा वास्तव में उठता है जब लोग बीन की गिनती के लिए एफपी का उपयोग करने की कोशिश करते हैं। यह उस के लिए काम करता है, लेकिन केवल अगर आप अभिन्न मूल्यों से चिपके रहते हैं, तो इसका उपयोग करने के बिंदु को किस तरह से हराया जाता है। यही कारण है कि हमारे पास उन सभी दशमलव अंश सॉफ्टवेयर लाइब्रेरी हैं।
मुझे क्रिस द्वारा पिज्जा का जवाब पसंद है , क्योंकि यह वास्तविक समस्या का वर्णन करता है, न कि केवल "अशुद्धि" के बारे में। अगर एफपी बस "गलत" थे, तो हम इसे ठीक कर सकते हैं और दशकों पहले कर सकते हैं। हमने ऐसा नहीं किया है क्योंकि एफपी प्रारूप कॉम्पैक्ट और तेज है और यह बहुत सारी संख्याओं को क्रंच करने का सबसे अच्छा तरीका है। इसके अलावा, यह अंतरिक्ष युग और हथियारों की दौड़ से एक विरासत है और छोटी स्मृति प्रणालियों का उपयोग करके बहुत धीमी गति से कंप्यूटर के साथ बड़ी समस्याओं को हल करने के शुरुआती प्रयास। (कभी-कभी, 1-बिट भंडारण के लिए व्यक्तिगत चुंबकीय कोर , लेकिन यह एक और कहानी है। )
निष्कर्ष
यदि आप किसी बैंक में सिर्फ बीन्स की गिनती कर रहे हैं, तो सॉफ्टवेयर सॉल्यूशंस जो पहले स्थान पर दशमलव स्ट्रिंग निरूपण का उपयोग करते हैं, पूरी तरह से अच्छी तरह से काम करते हैं। लेकिन आप इस तरह से क्वांटम क्रोमोडायनामिक्स या एरोडायनामिक्स नहीं कर सकते।
संक्षेप में यह है क्योंकि:
फ्लोटिंग पॉइंट नंबर बाइनरी में सभी दशमलव को ठीक से नहीं दर्शा सकते हैं
तो जैसे 3/10 जो बेस 10 में मौजूद नहीं है , ठीक है (यह 3.33 होगा ... आवर्ती), उसी तरह 1/10 बाइनरी में मौजूद नहीं है।
तो क्या? इसका सामना कैसे करें? क्या कोई वर्कअराउंड है?
सबसे अच्छा समाधान प्रदान करने के लिए मैं कह सकता हूं कि मैंने निम्नलिखित विधि की खोज की है:
parseFloat((0.1 + 0.2).toFixed(10)) => Will return 0.3
मुझे समझाएं कि यह सबसे अच्छा समाधान क्यों है। जैसा कि अन्य लोगों ने उपर्युक्त उत्तरों में बताया है कि समस्या को हल करने के लिए जावास्क्रिप्ट का उपयोग करने के लिए तैयार () फ़ंक्शन का उपयोग करने के लिए यह एक अच्छा विचार है। लेकिन सबसे अधिक संभावना है कि आप कुछ समस्याओं से सामना करेंगे।
कल्पना कीजिए कि आप दो फ़्लोट संख्याएँ जोड़ने जा रहे हैं जैसे 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
यह बिल्कुल भी काम नहीं करेगा, लेकिन ऐसा ही लगता है! मैं पहला समाधान पसंद करता हूं क्योंकि मैं इसे एक फ़ंक्शन के रूप में लागू कर सकता हूं जो इनपुट फ्लोट को सटीक आउटपुट फ्लोट में परिवर्तित करता है।
क्या आपने डक्ट टेप समाधान की कोशिश की?
यह निर्धारित करने का प्रयास करें कि कब त्रुटियां होती हैं और उन्हें कम करके ठीक करें यदि कथन, तो यह सुंदर नहीं है, लेकिन कुछ समस्याओं के लिए यह एकमात्र समाधान है और यह उनमें से एक है।
if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
else { return n * 0.1 + 0.000000000000001 ;}
मुझे सी # में एक वैज्ञानिक सिमुलेशन परियोजना में एक ही समस्या थी, और मैं आपको बता सकता हूं कि यदि आप तितली के प्रभाव को अनदेखा करते हैं, तो यह एक बड़े वसा वाले ड्रैगन की ओर जाता है और आपको ** में काटता है।
वे अजीब संख्याएं दिखाई देती हैं क्योंकि कंप्यूटर बाइनरी (बेस 2) नंबर सिस्टम का उपयोग गणना उद्देश्यों के लिए करते हैं, जबकि हम दशमलव (बेस 10) का उपयोग करते हैं।
बहुसंख्यक भिन्न संख्याएँ हैं जिनका बाइनरी या दशमलव या दोनों में सटीक रूप से प्रतिनिधित्व नहीं किया जा सकता है। परिणाम - एक गोल (लेकिन सटीक) संख्या परिणाम।
यह देखते हुए कि किसी ने भी इसका उल्लेख नहीं किया है ...
कुछ उच्च स्तरीय भाषाएं जैसे पायथन और जावा बाइनरी फ्लोटिंग पॉइंट सीमाओं को दूर करने के लिए टूल के साथ आते हैं। उदाहरण के लिए:
पायथन के 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)
जोड़े के रूप में तर्कसंगत संख्याओं का प्रतिनिधित्व करते हैं और वे दशमलव फ़्लोटिंग अंक अंकगणित की तुलना में अधिक सटीक परिणाम दे सकते हैं।
इन समाधानों में से कोई भी सही नहीं है (खासकर यदि हम प्रदर्शनों को देखते हैं, या अगर हमें बहुत उच्च परिशुद्धता की आवश्यकता होती है), लेकिन फिर भी वे बाइनरी फ्लोटिंग पॉइंट अंकगणित के साथ बड़ी संख्या में समस्याओं को हल करते हैं।
इस प्रश्न के कई डुप्लिकेट विशिष्ट संख्याओं पर फ़्लोटिंग पॉइंट राउंडिंग के प्रभावों के बारे में पूछते हैं। व्यवहार में, इसके बारे में सिर्फ पढ़ने के बजाय ब्याज की गणना के सटीक परिणामों को देखकर यह महसूस करना आसान है कि यह कैसे काम करता है। इस तरह के एक परिवर्तित रूप में - कुछ भाषाओं में है कि कुछ करने के तरीके प्रदान करते हैं 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५५५५५५५६५६५६६90६79 ९ ३५6५० ९ ०6१70०२50०५०125५५ में गोलाई देने की त्रुटि देती है। गोल-टू-सम टाई ब्रेकर लागू होता है।
फ़्लोटिंग पॉइंट कनवर्टर पर लौटना, 0.30000000000000004 के लिए कच्चा हेक्साडेसिमल 3fd3333333333334 है, जो एक सम अंक में समाप्त होता है और इसलिए सही परिणाम है।
क्या मैं सिर्फ जोड़ सकता हूं; लोग हमेशा इसे कंप्यूटर की समस्या मानते हैं, लेकिन अगर आप अपने हाथों (आधार 10) के साथ गिनते हैं, तो आप (1/3+1/3=2/3)=true
तब तक नहीं मिल सकते जब तक कि आपके पास 0.333 ... 0.333 ... जोड़ने की अनंतता न हो ... बस (1/10+2/10)!==3/10
आधार में समस्या के साथ २, आप इसे ०.३३३ + ०.३३३ = ०.६६६ पर छांटते हैं और संभवत: इसे ०.६६c पर गोल करते हैं जो तकनीकी रूप से गलत भी होगा।
टर्नरी में गिनती, और तिहाई हालांकि एक समस्या नहीं है - शायद प्रत्येक हाथ पर 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 अंतर में बैठता है।
पायथन 3.5 के बाद से आप math.isclose()
अनुमानित समानता के परीक्षण के लिए फ़ंक्शन का उपयोग कर सकते हैं :
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
>>> 0.1 + 0.2 == 0.3
False
आधार के साथ बेस दस में काम करने की कल्पना करें, कहते हैं, सटीकता के 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
, जो इस बात पर निर्भर करता है कि हमने प्रतिपादक और मंटिसा के लिए कितने बिट्स आवंटित किए हैं। यह प्रभावित करता है कि आप अपनी गणना के लिए सटीक कितने अंक प्राप्त करते हैं।
उतावलापन यह है कि इन गोल त्रुटियों के कारण आप अनिवार्य रूप से फ्लोटिंग-पॉइंट नंबरों पर == का उपयोग नहीं करना चाहते हैं। इसके बजाय, आप जांच सकते हैं कि उनके अंतर का निरपेक्ष मान कुछ निश्चित छोटी संख्या से छोटा है या नहीं।
जैसे दशमलव संख्या 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
प्रकार आपके सिस्टम पर उपलब्ध हो सकता है (उदाहरण के लिए, जीसीसी उन पर समर्थन करता है, चयनित लक्ष्य है, लेकिन बजना उन पर समर्थन नहीं करता है ओएस एक्स )।
चूंकि यह थ्रेड वर्तमान फ्लोटिंग पॉइंट कार्यान्वयन पर एक सामान्य चर्चा में थोड़ा आगे बढ़ गया है, इसलिए मुझे लगता है कि उनके मुद्दों को ठीक करने पर परियोजनाएं हैं।
पर एक नज़र डालें 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 सिस्टम में कंप्यूटर को संचालित कर रहे हैं, जब आप इन दोहराए जाने वाले दशमलव पर गणित करते हैं, तो आप बचे हुए के साथ समाप्त होते हैं। जब आप कंप्यूटर के आधार 2 (बाइनरी) संख्या को अधिक मानव पठनीय आधार 10 नंबर में परिवर्तित करते हैं, तो इसे आगे बढ़ाया जाता है।
से https://0.30000000000000004.com/
सामान्य अंकगणित आधार -10 है, इसलिए दशमलव दशमांश, सौवें आदि का प्रतिनिधित्व करते हैं। जब आप बाइनरी बेस -2 अंकगणित में एक फ्लोटिंग-पॉइंट संख्या का प्रतिनिधित्व करने का प्रयास करते हैं, तो आप हाफ़, फोर्थ, इगथ, आदि के साथ काम कर रहे हैं।
हार्डवेयर में, फ्लोटिंग पॉइंट्स को पूर्णांक मंटिस और एक्सप्लॉर्स के रूप में संग्रहीत किया जाता है। मंटिसा महत्वपूर्ण अंकों का प्रतिनिधित्व करती है। घातांक वैज्ञानिक संकेतन की तरह है, लेकिन यह 10. के बजाय 2 के आधार का उपयोग करता है। उदाहरण के लिए 64.0 को 1 के मंटिसा के साथ दर्शाया जाएगा और 6. के घातांक का प्रतिनिधित्व किया जाएगा।
फ्लोटिंग पॉइंट डेसीमल को 2 की नकारात्मक शक्तियों को जोड़ना है
0.1b = 0.5d
0.01b = 0.25d
0.001b = 0.125d
0.0001b = 0.0625d
0.00001b = 0.03125d
और इसी तरह।
फ़्लोटिंग पॉइंट अंकगणित से निपटने के दौरान समानता ऑपरेटरों का उपयोग करने के बजाय एक त्रुटि डेल्टा का उपयोग करना आम है। के बजाय
if(a==b) ...
आप उपयोग करेंगे
delta = 0.0001; // or some arbitrarily small amount
if(a - b > -delta && a - b < delta) ...
बाइनरी पॉइंट (बेस 2) के अंशों के रूप में, हार्डवेयर स्तर पर फ्लोटिंग पॉइंट नंबरों का प्रतिनिधित्व किया जाता है। उदाहरण के लिए, दशमलव अंश:
0.125
मूल्य 1/10 + 2/100 + 5/1000 है और उसी तरह, द्विआधारी अंश:
0.001
मान 0/2 + 0/4 + 1/8 है। इन दो अंशों का एक ही मूल्य है, केवल अंतर यह है कि पहला दशमलव अंश है, दूसरा द्विआधारी अंश है।
दुर्भाग्य से, अधिकांश दशमलव अंशों में बाइनरी अंशों में सटीक प्रतिनिधित्व नहीं हो सकता है। इसलिए, सामान्य तौर पर, आप जो फ्लोटिंग पॉइंट नंबर देते हैं, वह मशीन में स्टोर किए जाने के लिए केवल बाइनरी फ्रैक्शंस के लिए अनुमानित होता है।
समस्या को आधार 10 में जाना आसान है। उदाहरण के लिए, अंश 1/3 लीजिए। आप इसे दशमलव अंश तक अनुमानित कर सकते हैं:
0.3
या और अच्छा,
0.33
या और अच्छा,
0.333
आदि कोई फर्क नहीं पड़ता कि आप कितने दशमलव स्थान लिखते हैं, परिणाम कभी भी 1/3 नहीं होता है, लेकिन यह एक अनुमान है कि हमेशा करीब आता है।
इसी तरह, चाहे आप कितने भी आधार 2 दशमलव स्थानों का उपयोग करें, दशमलव मान 0.1 का बाइनरी अंश के रूप में प्रतिनिधित्व नहीं किया जा सकता है। आधार 2 में, 1/10 निम्नलिखित आवधिक संख्या है:
0.0001100110011001100110011001100110011001100110011 ...
बिट्स के किसी भी परिमित राशि पर रुकें, और आपको एक अनुमान मिलेगा।
पायथन के लिए, एक विशिष्ट मशीन पर, फ्लोट की शुद्धता के लिए 53 बिट्स का उपयोग किया जाता है, इसलिए जब आप दशमलव 0.1 में प्रवेश करते हैं तो संग्रहीत मूल्य द्विआधारी अंश होता है।
0.00011001100110011001100110011001100110011001100110011010
जो करीब है, लेकिन 1/10 के बराबर नहीं है।
यह भूलना आसान है कि संग्रहीत मूल्य मूल दशमलव अंश का एक अनुमान है, जिस तरह से दुभाषिया में फ्लोट प्रदर्शित किए जाते हैं। पायथन केवल बाइनरी में संग्रहीत मूल्य का एक दशमलव अनुमान प्रदर्शित करता है। यदि पायथन बाइनरी सन्निकटन के वास्तविक दशमलव मान को 0.1 के लिए संग्रहीत करता है, तो यह आउटपुट होगा:
>>> 0.1
0.1000000000000000055511151231257827021181583404541015625
यह उन लोगों की अपेक्षा बहुत अधिक दशमलव स्थान है, जिनकी अपेक्षा पायथन ने पठनीयता में सुधार करने के लिए एक गोल मूल्य प्रदर्शित किया है:
>>> 0.1
0.1
यह समझना महत्वपूर्ण है कि वास्तव में यह एक भ्रम है: संग्रहीत मूल्य बिल्कुल 1/10 नहीं है, यह केवल प्रदर्शन पर है कि संग्रहीत मूल्य गोल है। जैसे ही आप इन मूल्यों के साथ अंकगणितीय ऑपरेशन करते हैं, यह स्पष्ट हो जाता है:
>>> 0.1 + 0.2
0.30000000000000004
यह व्यवहार मशीन के फ्लोटिंग-पॉइंट प्रतिनिधित्व की बहुत प्रकृति के लिए अंतर्निहित है: यह पायथन में एक बग नहीं है, न ही यह आपके कोड में एक बग है। आप अन्य सभी भाषाओं में उसी प्रकार के व्यवहार का निरीक्षण कर सकते हैं जो फ्लोटिंग पॉइंट नंबरों की गणना के लिए हार्डवेयर समर्थन का उपयोग करते हैं (हालांकि कुछ भाषाएं डिफ़ॉल्ट रूप से दिखाई देने वाले अंतर को नहीं दिखाती हैं, या सभी डिस्प्ले मोड में नहीं हैं)।
एक और आश्चर्य इस एक में निहित है। उदाहरण के लिए, यदि आप मान 2.675 को दो दशमलव स्थानों पर गोल करने का प्रयास करते हैं, तो आपको मिलेगा
>>> round (2.675, 2)
2.67
दौर के लिए प्रलेखन () आदिम इंगित करता है कि यह शून्य से दूर निकटतम मूल्य के लिए गोल है। चूंकि दशमलव अंश 2.67 और 2.68 के बीच बिल्कुल आधा है, इसलिए आपको 2.68 प्राप्त करने की उम्मीद करनी चाहिए। हालाँकि, यह ऐसा नहीं है, क्योंकि जब दशमलव अंश 2.675 को एक फ्लोट में बदल दिया जाता है, तो इसे एक अनुमान द्वारा संग्रहीत किया जाता है जिसका सटीक मान है:
2.67499999999999982236431605997495353221893310546875
चूंकि अनुमान 2.68 की तुलना में 2.67 के करीब है, गोलाई नीचे है।
यदि आप ऐसी स्थिति में हैं जहां दशमलव संख्याओं को आधा करना मामलों से कम है, तो आपको दशमलव मॉड्यूल का उपयोग करना चाहिए। वैसे, दशमलव मॉड्यूल किसी भी फ्लोट के लिए संग्रहीत सटीक मान को "देखने" के लिए एक सुविधाजनक तरीका प्रदान करता है।
>>> from decimal import Decimal
>>> Decimal (2.675)
>>> Decimal ('2.67499999999999982236431605997495353221893310546875')
इस तथ्य का एक और परिणाम यह है कि 0.1 बिल्कुल 1/10 में संग्रहीत नहीं है, 0.1 के दस मानों का योग 1.0 भी नहीं देता है:
>>> sum = 0.0
>>> for i in range (10):
... sum + = 0.1
...>>> sum
0.9999999999999999
बाइनरी फ्लोटिंग पॉइंट संख्याओं के अंकगणित में कई ऐसे आश्चर्य हैं। "0.1" के साथ समस्या को नीचे "खंड त्रुटियों" में विस्तार से समझाया गया है। इस तरह के आश्चर्य की एक पूरी सूची के लिए फ्लोटिंग पॉइंट के पर्ल्स देखें।
यह सच है कि कोई सरल उत्तर नहीं है, हालांकि अस्थायी पुण्य संख्याओं पर अत्यधिक संदेह नहीं है! पायथन में त्रुटियां, फ़्लोटिंग-पॉइंट संख्या में ऑपरेशन अंतर्निहित हार्डवेयर के कारण होती हैं, और अधिकांश मशीनों पर ऑपरेशन 2 में 1 से अधिक नहीं होता है। 53 प्रति ऑपरेशन। यह अधिकांश कार्यों के लिए आवश्यक से अधिक है, लेकिन आपको यह ध्यान रखना चाहिए कि ये दशमलव संचालन नहीं हैं, और फ़्लोटिंग पॉइंट संख्या पर प्रत्येक ऑपरेशन एक नई त्रुटि से पीड़ित हो सकता है।
यद्यपि पैथोलॉजिकल मामले मौजूद हैं, अधिकांश सामान्य उपयोग के मामलों के लिए आपको प्रदर्शन पर इच्छित दशमलव स्थानों की संख्या को केवल गोल करके अंत में अपेक्षित परिणाम मिलेगा। फ़्लोट कैसे प्रदर्शित होता है, इस पर ठीक नियंत्रण के लिए, स्ट्रिंग प्रारूपण सिंटैक्स को देखें।
जवाब का यह हिस्सा "0.1" के उदाहरण के बारे में विस्तार से बताता है और दिखाता है कि आप इस तरह के मामले का सटीक विश्लेषण कैसे कर सकते हैं। हम मानते हैं कि आप फ़्लोटिंग पॉइंट नंबरों के बाइनरी प्रतिनिधित्व से परिचित हैं। शब्द प्रतिनिधित्व त्रुटि का अर्थ है कि अधिकांश दशमलव अंशों को बाइनरी में बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है। यह मुख्य कारण है कि पायथन (या पर्ल, सी, सी ++, जावा, फोरट्रान और कई अन्य) आमतौर पर दशमलव में सटीक परिणाम प्रदर्शित नहीं करते हैं:
>>> 0.1 + 0.2
0.30000000000000004
क्यों ? बाइनरी अंशों में 1/10 और 2/10 प्रतिनिधित्व योग्य नहीं हैं। हालाँकि, आज (जुलाई 2010) सभी मशीनें अस्थायी बिंदु संख्याओं के अंकगणित के लिए IEEE-754 मानक का पालन करती हैं। और अधिकांश प्लेटफ़ॉर्म पायथन फ़्लोट्स का प्रतिनिधित्व करने के लिए "IEEE-754 दोहरी परिशुद्धता" का उपयोग करते हैं। डबल सटीक आईईईई -754 53 बिट्स का सटीक उपयोग करता है, इसलिए कंप्यूटर को पढ़ने पर फॉर्म जे / 2 ** एन के निकटतम हिस्से में 0.1 को जे 53 बिट्स के पूर्णांक के साथ बदलने की कोशिश करता है। फिर से लिखें:
1/10 ~ = J / (2 ** N)
में:
J ~ = 2 ** N / 10
यह याद रखना कि J वास्तव में 53 बिट्स है (इसलिए> = 2 ** 52 लेकिन <2 ** 53), N के लिए सर्वोत्तम संभव मान 56 है:
>>> 2 ** 52
4503599627370496
>>> 2 ** 53
9007199254740992
>>> 2 ** 56/10
7205759403792793
तो 56 N के लिए एकमात्र संभव मान है जो J. के लिए बिल्कुल 53 बिट्स छोड़ता है। J के लिए सर्वोत्तम संभव मान इसलिए यह भागफल, गोल है:
>>> q, r = divmod (2 ** 56, 10)
>>> r
6
चूँकि कैरी 10 के आधे से अधिक है, इसलिए सबसे अच्छा सन्निकटन गोलाई द्वारा प्राप्त किया जाता है:
>>> q + 1
7205759403792794
इसलिए "IEEE-754 दोहरी परिशुद्धता" में 1/10 के लिए सबसे अच्छा संभव अनुमान 2 ** 56 से ऊपर है, अर्थात:
7205759403792794/72057594037927936
ध्यान दें कि चूंकि गोलाई ऊपर की ओर की गई थी, परिणाम वास्तव में 1/10 से थोड़ा अधिक है; यदि हम गोल नहीं होते, तो भागफल 1/10 से थोड़ा कम होता। लेकिन किसी भी मामले में यह 1/10 नहीं है!
इसलिए कंप्यूटर कभी भी "1" नहीं देखता है: यह जो देखता है वह ऊपर दिया गया सटीक अंश है, "" IEEE-754 "से दोहरे सटीक फ्लोटिंग पॉइंट नंबरों का उपयोग करते हुए सबसे अच्छा सन्निकटन:"
>>>. 1 * 2 ** 56
7205759403792794.0
यदि हम इस अंश को 10 ** 30 से गुणा करते हैं, तो हम इसके 30 दशमलव स्थानों के मजबूत वजन के मूल्यों का पालन कर सकते हैं।
>>> 7205759403792794 * 10 ** 30 // 2 ** 56
100000000000000005551115123125L
अर्थ है कि कंप्यूटर में संग्रहीत सटीक मान दशमलव मान 0.100000000000000005551115123125 के लगभग बराबर है। पायथन 2.7 और पायथन 3.1 से पहले के संस्करणों में, पायथन ने इन मूल्यों को 17 महत्वपूर्ण दशमलव स्थानों पर गोल किया, "0.10000000000000001" प्रदर्शित किया। पाइथन के वर्तमान संस्करणों में, प्रदर्शित मूल्य वह मूल्य है जिसका अंश जितना संभव हो उतना कम है, जबकि बाइनरी में परिवर्तित होने पर बिल्कुल समान प्रतिनिधित्व देते हैं, बस "0.1" प्रदर्शित करते हैं।
मैंने इस दिलचस्प मुद्दे को फ्लोटिंग पॉइंट्स के आसपास देखा:
निम्नलिखित परिणामों पर विचार करें:
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 स्पष्ट रूप से संग्रहीत)
किसी दिए गए पक्षपाती प्रतिपादक और एक 52-बिट अंश के साथ एक 64-बिट डबल-सटीक डेटम द्वारा ग्रहण किया गया वास्तविक मूल्य
या
मेरी ओर इशारा करने के लिए @a_guest का धन्यवाद।