0.1 बार जोड़ने पर दोषरहित क्यों रहता है?


152

मुझे पता है कि 0.1दशमलव संख्या को एक परिमित बाइनरी नंबर ( स्पष्टीकरण ) के साथ बिल्कुल नहीं दर्शाया जा सकता है , इसलिए double n = 0.1कुछ सटीकता खो जाएगी और बिल्कुल नहीं होगी 0.1। दूसरी तरफ 0.5इसका सही प्रतिनिधित्व किया जा सकता है क्योंकि यह है 0.5 = 1/2 = 0.1b

यह कहते हुए कि यह समझा जा सकता है कि 0.1 तीन बार जोड़ने से वास्तव 0.3में निम्नलिखित कोड प्रिंट नहीं होंगे false:

double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
    sum += d;
System.out.println(sum == 0.3); // Prints false, OK

लेकिन फिर यह कैसे है कि 0.1 पांच बार जोड़ने से वास्तव में मिल जाएगा 0.5? निम्नलिखित कोड प्रिंट करता है true:

double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
    sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?

यदि 0.1वास्तव में प्रतिनिधित्व नहीं किया जा सकता है, तो यह कैसे है कि इसे 5 बार जोड़ने से वास्तव में 0.5इसका प्रतिनिधित्व किया जा सकता है?


7
यदि आप वास्तव में इस पर शोध करते हैं तो मुझे यकीन है कि आप इसका पता लगा सकते हैं, लेकिन फ्लोटिंग पॉइंट "आश्चर्य" से भरा हुआ है, और कभी-कभी यह सिर्फ आश्चर्य में देखना बेहतर होता है।
हॉट लिप्स

3
आप इस बारे में सोच रहे हैं। फ्लोटिंग पॉइंट आर्किटिक्स किसी भी तरह से गणित नहीं है।
जैकब

13
@HotLicks जो बहुत गलत रवैया है।
हॉब्स

2
@RussellBorogove भले ही इसे अनुकूलित किया गया था, यह केवल एक वैध अनुकूलन होगा यदि sumअंतिम मान समान था जैसे कि लूप को वास्तव में निष्पादित किया गया था। C ++ मानक में इसे "as-if नियम" या "समान अवलोकन व्यवहार" कहा जाता है।
हॉब्स

7
@ जाकोब बिल्कुल भी सच नहीं है। फ़्लोटिंग-पॉइंट अंकगणित को कठोरता से परिभाषित किया गया है, त्रुटि सीमा और इस तरह के अच्छे गणितीय उपचार के साथ। यह सिर्फ इतना है कि कई प्रोग्रामर या तो विश्लेषण के माध्यम से अनुसरण करने के लिए तैयार नहीं हैं, या वे गलती से मानते हैं कि "फ्लोटिंग-पॉइंट अक्षम है" यह सब जानना है और यह विश्लेषण के साथ परेशान करने लायक नहीं है।
हॉब्स

जवाबों:


155

राउंडिंग त्रुटि यादृच्छिक नहीं है और इसे लागू करने का तरीका त्रुटि को कम करने का प्रयास करता है। इसका मतलब है कि कभी-कभी त्रुटि दिखाई नहीं देती है, या त्रुटि नहीं होती है।

उदाहरण के लिए 0.1वास्तव में नहीं है 0.1यानी new BigDecimal("0.1") < new BigDecimal(0.1)लेकिन 0.5ठीक है1.0/2

यह कार्यक्रम आपको शामिल सच्चे मूल्यों को दिखाता है।

BigDecimal _0_1 = new BigDecimal(0.1);
BigDecimal x = _0_1;
for(int i = 1; i <= 10; i ++) {
    System.out.println(i+" x 0.1 is "+x+", as double "+x.doubleValue());
    x = x.add(_0_1);
}

प्रिंट

0.1000000000000000055511151231257827021181583404541015625, as double 0.1
0.2000000000000000111022302462515654042363166809082031250, as double 0.2
0.3000000000000000166533453693773481063544750213623046875, as double 0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500, as double 0.4
0.5000000000000000277555756156289135105907917022705078125, as double 0.5
0.6000000000000000333066907387546962127089500427246093750, as double 0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375, as double 0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000, as double 0.8
0.9000000000000000499600361081320443190634250640869140625, as double 0.9
1.0000000000000000555111512312578270211815834045410156250, as double 1.0

नोट: 0.3यह थोड़ा बंद है, लेकिन जब आप 0.4बिट्स में पहुंचते हैं तो 53-बिट सीमा में फिट होने के लिए एक को शिफ्ट करना पड़ता है और त्रुटि को छोड़ दिया जाता है। फिर, एक त्रुटि ढोंगी के लिए में वापस 0.6और 0.7लेकिन के लिए 0.8करने के लिए 1.0त्रुटि खारिज कर दिया है।

इसे 5 बार जोड़ना त्रुटि को कम करना चाहिए, इसे रद्द नहीं करना चाहिए।

सीमित परिशुद्धता के कारण त्रुटि होने का कारण है। यानी 53-बिट्स। इसका मतलब यह है कि जैसे-जैसे संख्या अधिक होती जाती है, वैसे-वैसे बड़े होते जाते हैं, बिट्स को अंत में छोड़ना पड़ता है। यह गोलाई का कारण बनता है जो इस मामले में आपके पक्ष में है।
छोटी संख्या जैसे 0.1-0.0999=> प्राप्त करते समय आप विपरीत प्रभाव प्राप्त कर सकते हैं 1.0000000000000286E-4 और आप पहले से अधिक त्रुटि देखते हैं।

इसका एक उदाहरण जावा 6 में क्यों है। Math.round (0.49999999999999994) 1 क्यों लौटता है, इस मामले में गणना में थोड़ा सा नुकसान होने से जवाब में बड़ा अंतर आ जाता है।


1
यह कहां लागू किया गया है?
एपिकपांडाफायर

16
@Zhuinden CPU IEEE-754 मानक का अनुसरण करता है। जावा आपको अंतर्निहित सीपीयू निर्देशों तक पहुंच प्रदान करता है और इसमें शामिल नहीं होता है। en.wikipedia.org/wiki/IEEE_floating_point
पीटर लॉरी

10
@PeterLawrey: जरूरी नहीं कि सी.पी.यू. सीपीयू में फ्लोटिंग पॉइंट के बिना एक मशीन पर (और उपयोग में कोई अलग FPU), सॉफ्टवेयर द्वारा IEEE अंकगणित नहीं किया जाएगा। और यदि होस्ट CPU में फ्लोटिंग पॉइंट है, लेकिन यह IEEE आवश्यकताओं के अनुरूप नहीं है, तो मुझे लगता है कि उस CPU के लिए जावा कार्यान्वयन सॉफ्ट फ्लोट का भी उपयोग करने के लिए बाध्य होगा ...
R .. GitHub STOP HELPING ICE

1
@ आर .. जिस स्थिति में मुझे नहीं पता कि अगर आप strictfp समय का इस्तेमाल करते हैं तो मुझे लगता है कि निश्चित बिंदु पूर्णांक पर विचार करने के लिए क्या होगा । (या
बिगडेसीमल

2
@ प्रमुख समस्या यह है कि सीमित मान फ्लोटिंग पॉइंट का प्रतिनिधित्व कर सकता है। इस सीमा के परिणामस्वरूप जानकारी का नुकसान हो सकता है और जैसे ही संख्या में त्रुटि होती है। यह राउंडिंग का उपयोग करता है, लेकिन इस मामले में, राउंड डाउन होता है, तो ऐसी संख्या क्या होती है जो थोड़ी बहुत बड़ी होती है क्योंकि 0.1 थोड़ी बहुत बड़ी होती है, सही मान में बदल जाती है। ठीक 0.5
पीटर लॉरी

47

फ्लोइंग-पॉइंट में बैरिंग ओवरफ्लो, x + x + xवास्तविक 3 * के लिए ठीक से गोल (यानी निकटतम) फ्लोटिंग-पॉइंट नंबर है x, x + x + x + xबिल्कुल 4 * है x, और x + x + x + x + x5 * के लिए फिर से सही ढंग से राउंड-फ्लोटिंग-पॉइंट सन्निकटन है x

पहला परिणाम, x + x + xइस तथ्य से है, जो x + xसटीक है। x + x + xइस प्रकार केवल एक गोलाई का परिणाम है।

दूसरा परिणाम अधिक कठिन है, इसका एक प्रदर्शन यहां चर्चा की गई है (और स्टीफन कैनन पिछले 3 अंकों पर मामले के विश्लेषण से एक और प्रमाण के लिए संकेत देता है x)। संक्षेप में, या तो 3 * 2 के रूप xमें एक ही बेनेड में है * xया यह 4 * के रूप में एक ही बायनेड में है x, और प्रत्येक मामले में यह घटाना संभव है कि तीसरे जोड़ पर त्रुटि दूसरे जोड़ पर त्रुटि को रद्द कर देती है ( पहला जोड़ सटीक होना, जैसा कि हमने पहले ही कहा था)।

तीसरा परिणाम, " x + x + x + x + xसही ढंग से गोल किया गया है", दूसरे से उसी तरह निकलता है जैसे कि पहला शब्द सटीकता से मिलता है x + x


दूसरा परिणाम बताता है कि 0.1 + 0.1 + 0.1 + 0.1फ्लोटिंग-पॉइंट संख्या क्यों है 0.4: तर्कसंगत संख्या 1/10 और 4/10 उसी तरह अनुमानित हो जाती है, जब समान सापेक्ष त्रुटि के साथ, फ़्लोटिंग-पॉइंट में परिवर्तित हो जाती है। इन फ़्लोटिंग-पॉइंट नंबरों का अनुपात उनके बीच ठीक 4 का है। पहले और तीसरे परिणाम से पता चलता है कि 0.1 + 0.1 + 0.1और 0.1 + 0.1 + 0.1 + 0.1 + 0.1भोले-भाले त्रुटि विश्लेषण द्वारा अनुमान से कम त्रुटि होने की उम्मीद की जा सकती है, लेकिन, स्वयं में, वे केवल क्रमशः परिणामों से संबंधित होते हैं 3 * 0.1और 5 * 0.1, जो करीब होने की उम्मीद कर सकते हैं, लेकिन जरूरी नहीं कि समान 0.3और 0.5

यदि आप 0.1चौथे जोड़ के बाद भी जोड़ते रहते हैं , तो आप अंत में उन गोल त्रुटियों का निरीक्षण करेंगे जो “ 0.1n n से खुद को जोड़ते हैं” डायवर्ज करते हैं n * 0.1, और n / 10 से भी अधिक डायवर्ज करते हैं। यदि आप n के एक फंक्शन के रूप में "0.1 अपने आप को n बार" में जोड़ने की साजिश रच रहे थे, तो आप बायनेड्स द्वारा निरंतर ढलान की रेखाओं का निरीक्षण करेंगे (जैसे ही nth जोड़ का परिणाम एक विशेष बायनेड में गिरना तय है,) इसके अतिरिक्त गुणधर्मों को पिछले जोड़ के समान होने की उम्मीद की जा सकती है जो एक ही बायनाड में परिणाम उत्पन्न करते हैं)। एक ही बायनेड के भीतर, त्रुटि या तो बढ़ेगी या सिकुड़ जाएगी। यदि आप बायनेड से बायनेड के ढलान के अनुक्रम को देखते थे, तो आप दोहराए जाने वाले अंकों को पहचान लेंगे0.1कुछ समय के लिए द्विआधारी में उसके बाद, अवशोषण शुरू हो जाएगा और वक्र सपाट हो जाएगा।


1
पहली पंक्ति पर आप कह रहे हैं कि x + x + x बिल्कुल सही है, लेकिन उदाहरण में प्रश्न में यह नहीं है।
अलबोज़

2
@Alboz मैं कहता हूं कि वास्तविक 3 * के लिए x + x + xबिल्कुल सही ढंग से राउंड -फ्लोटिंग-पॉइंट नंबर है x। "सही ढंग से गोल" का अर्थ इस संदर्भ में "निकटतम" है।
पास्कल क्यूक सिप

4
+1 यह स्वीकृत उत्तर होना चाहिए। यह वास्तव में केवल अस्पष्ट सामान्यताओं के बजाय क्या हो रहा है, इसका स्पष्टीकरण / प्रमाण प्रस्तुत करता है।
R .. गिटहब स्टॉप हेल्पिंग ICE

1
@ एब्बोज़ (जो सभी प्रश्न द्वारा कल्पना की गई है)। लेकिन यह उत्तर बताता है कि सबसे खराब स्थिति में गलतियाँ करने के बजाय त्रुटियों को कैसे रद्द किया जाता है।
हॉब्स

1
@chebus 0.1 है 0x1.99999999999999999999999 ... हेक्साडेसिमल में पी -4 (अंकों का एक अनंत अनुक्रम)। यह 0x1.99999ap-4 के रूप में डबल-परिशुद्धता में अनुमानित है। 0.2 हेक्साडेसिमल में 0x1.99999999999999999999999 है ... पी -3। उसी कारण से कि 0.1 को 0x1.99999ap-4 के रूप में अनुमानित किया गया है, 0.2 को 0x1.99999ap-3 के रूप में अनुमानित किया गया है। इस बीच, 0x1.99999ap-3 भी बिल्कुल 0x1.99999ap-4 + 0x1.99999ap-4 है।
पास्कल क्यूक

-1

फ़्लोटिंग पॉइंट सिस्टम गोलाई के लिए कुछ अतिरिक्त बिट्स के सटीक होने सहित विभिन्न जादू करते हैं। इस प्रकार 0.1 के अथाह निरूपण के कारण बहुत ही छोटी त्रुटि 0.5 से गोल हो जाती है।

संख्याओं का प्रतिनिधित्व करने के लिए एक शानदार लेकिन INEXACT तरीका होने के रूप में फ़्लोटिंग पॉइंट के बारे में सोचें। कंप्यूटर में सभी संभव संख्याओं का आसानी से प्रतिनिधित्व नहीं किया जाता है। PI जैसे अपरिमेय संख्या। या SQRT (2) की तरह। (प्रतीकात्मक गणित प्रणाली उनका प्रतिनिधित्व कर सकती है, लेकिन मैंने "आसानी से" कहा।)

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

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

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

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

गिनती के अनुप्रयोगों के लिए (लेखांकन सहित) पूर्णांक का उपयोग करें। एक गेट से गुजरने वाले लोगों की संख्या की गणना के लिए, इंट या लंबे का उपयोग करें।


2
प्रश्न टैग किया गया है [जावा]। जावा भाषा की परिभाषा में "कुछ अतिरिक्त बिट्स ऑफ प्रिसिजन" का प्रावधान नहीं है , केवल कुछ अतिरिक्त एक्सपोनेंट बिट्स के लिए (और यह केवल तभी है जब आप उपयोग नहीं करते हैं strictfp)। सिर्फ इसलिए कि आपने कुछ समझने के लिए त्याग किया है इसका मतलब यह नहीं है कि यह अथाह है और न ही दूसरों को इसे समझने के लिए त्याग करना चाहिए। देखें stackoverflow.com/questions/18496560 लंबाई जावा कार्यान्वयन आदेश भाषा परिभाषा (जो अतिरिक्त परिशुद्धता बिट्स के लिए किसी भी प्रावधान है और न ही, साथ शामिल नहीं है को लागू करने में के लिए जाना जाएगा का एक उदाहरण के रूप में strictfp, किसी भी अतिरिक्त exp बिट के लिए)
पास्कल Cuoq
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.