2010-05-27 9 views
9

पर मॉड्यूलो ऑपरेटर तो मैं यह पता लगाने की कोशिश कर रहा हूं कि मॉड्यूलो ऑपरेटर इतना बड़ा असामान्य मूल्य क्यों लौट रहा है।फ़्लोटिंग प्वाइंट अंकगणित - डबल टाइप

अगर मैं कोड है:

double result = 1.0d % 0.1d;

यह 0.09999999999999995 का एक परिणाम दे देंगे। मैं की 0

नोट एक मूल्य के इस समस्या को विभाजन ऑपरेटर का उपयोग मौजूद नहीं है उम्मीद करेंगे - double result = 1.0d/0.1d;

10.0 का एक परिणाम दे देंगे, जिसका अर्थ है कि शेष 0 होना चाहिए।

मुझे स्पष्ट होने दें: मुझे आश्चर्य नहीं है कि एक त्रुटि मौजूद है, मुझे आश्चर्य है कि त्रुटि बड़ी संख्या खेलने की संख्या की तुलना में बहुत कम है। 0.099 9 ~ = 0.1 और 0.1 0.1d के रूप में परिमाण के समान क्रम पर और 1.0d से परिमाण का केवल एक क्रम है। ऐसा नहीं है कि आप इसकी तुलना डबल.पिसिलॉन से कर सकते हैं, या कहें "इसके बराबर अगर < 0.00001 अंतर"।

मैंने निम्नलिखित पदों में निम्नलिखित पोस्ट onetwothree में, इस विषय पर StackOverflow पर पढ़ा है।

क्या कोई यह बता सकता है कि यह त्रुटि इतनी बड़ी क्यों है? भविष्य में समस्याओं में भागने से बचने के लिए कोई भी सुझाव (मुझे पता है कि मैं इसके बजाय दशमलव का उपयोग कर सकता हूं लेकिन मैं इसके प्रदर्शन के बारे में चिंतित हूं)।

संपादित करें: मुझे विशेष रूप से यह इंगित करना चाहिए कि मुझे पता है कि 0.1 infinitely repeating series of numbers in binary है - क्या इसका इसके साथ कुछ लेना देना है?

उत्तर

14

त्रुटि इस बात के बारे में आती है क्योंकि एक डबल 0.1 का प्रतिनिधित्व नहीं कर सकता है - निकटतम यह प्रतिनिधित्व कर सकता है 0.1000000000000005551115123126 जैसा कुछ है। अब जब आप 1.0 को विभाजित करते हैं तो यह आपको 10 से थोड़ा कम संख्या देता है, लेकिन फिर एक डबल वास्तव में इसका प्रतिनिधित्व नहीं कर सकता है, इसलिए यह 10 तक बढ़ता है। लेकिन जब आप मॉड करते हैं, तो यह आपको थोड़ा सा दे सकता है 0.1 से कम शेष।

0 = 0.1 मॉड 0.1 के बाद, मॉड में वास्तविक त्रुटि 0.1 - 0.0 99 99 999 है ... - बहुत छोटा।

यदि आप% ऑपरेटर का परिणाम 9 * 0.1 में जोड़ते हैं, तो यह आपको 1.0 फिर से देगा। राउंडिंग सामान पर

संपादित

थोड़ा और अधिक विस्तार - इस समस्या को मिश्रित परिशुद्धता के खतरों का एक अच्छा उदाहरण है विशेष रूप से के रूप में।

फ़्लोटिंग पॉइंट नंबरों के लिए a % b आमतौर पर गणना की जाती है a - (b * floor(a/b))। समस्या यह है कि यह उन सभी परिचालनों के साथ अधिक आंतरिक परिशुद्धता के साथ एक साथ किया जा सकता है (और परिणाम को प्रत्येक चरण में एक एफपी संख्या में गोल करना), इसलिए यह आपको एक अलग परिणाम दे सकता है। एक उदाहरण है कि बहुत से लोग इंटेल x86/x87 हार्डवेयर के साथ इंटरमीडिएट कंप्यूटेशंस के लिए 80-बिट परिशुद्धता का उपयोग कर रहे हैं और स्मृति में मानों के लिए केवल 64-बिट परिशुद्धता का उपयोग कर रहे हैं। (80 बिट के लिए गोल तो उपरोक्त समीकरण में b में मूल्य स्मृति से आ रही है और इस तरह एक 64-बिट fp संख्या कि नहीं है काफी 0.1 (सटीक मूल्य के लिए dan04 धन्यवाद), इसलिए जब यह 1.0/0.1 यह 9,99999999999999944488848768742172978818416595458984375 हो जाता है की गणना करता है)। अब यदि आप इसे 64 बिट्स तक ले जाते हैं, तो यह 10.0 होगा, लेकिन यदि आप 80 बिट आंतरिक रखते हैं और उस पर फर्श करते हैं, तो यह 9.0 तक गिर जाता है और इस प्रकार अंतिम जवाब के रूप में .0 99 99 99 99 99 99 99 9539399638918679556809365749359130859375 हो जाता है।

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

+0

0.1000000000000000055511151231257827021181583404541015625, सटीक होने के लिए। – dan04

+0

मैं गणना के चलने की सराहना करता हूं। एक स्पष्टीकरण - आप उल्लेख करते हैं "0 = 0.1 मॉड 0.1 के बाद, मॉड में वास्तविक त्रुटि 0.1 - 0.0 99 99 999 है ... - बहुत छोटा।" मैं इससे उलझन में हूं क्योंकि मेरा परिणाम लौटा 0.099 999 था, लेकिन वास्तविक परिणाम 0 है। वास्तविक त्रुटि 0.1 - 0.0 99 999 कैसे है? क्या आप थोड़ा और विस्तार से इस हिस्से की त्रुटि गणना को तोड़ सकते हैं? भविष्य में पाठकों के लिए पिछली टिप्पणी का जवाब देने के लिए – CrimsonX

+0

: संपादन अनुभाग इसका उत्तर देता है। (संक्षिप्त स्पष्टीकरण: त्रुटि की अस्थिरता के कारण त्रुटि बड़ी है (देखा हुआ डेंट फ़ंक्शन) 'ए' (उदा। 10.0001) में मामूली वृद्धि परिणाम को 0 के करीब गिर जाएगी) –

2

यह गणना में बिल्कुल "त्रुटि" नहीं है लेकिन तथ्य यह है कि आपके पास वास्तव में 0.1 से शुरू नहीं हुआ था।

समस्या यह है कि 1.0 को बाइनरी फ्लोटिंग पॉइंट में बिल्कुल प्रदर्शित किया जा सकता है लेकिन 0.1 नहीं कर सकता, क्योंकि इसे दो की नकारात्मक शक्तियों से बिल्कुल नहीं बनाया जा सकता है। (यह 1/16 + 1/32 + ...)

तो आपको वास्तव में 1.0% 0.1 नहीं मिल रहा है, मशीन 1.0% 0.1 + - 0.00 की गणना करने के लिए छोड़ी गई है ... और फिर यह ईमानदारी से रिपोर्ट करता है परिणामस्वरूप ...

एक बड़ा शेष होने के लिए, मुझे लगता है कि % का दूसरा ऑपरेंड 0.1 से थोड़ा अधिक होना चाहिए, अंतिम विभाजन को रोकना, और जिसके परिणामस्वरूप लगभग 0.1 आपरेशन।

+0

लेकिन क्यों होता 1.0% 0.1 शो इस त्रुटि और 1.0/0.1 त्रुटि नहीं दिखा? – CrimsonX

+0

मुझे लगता है कि आपका मतलब है कि "मशीन 1.0% 0.0 999 की गणना करने के लिए छोड़ी गई है ..." – CrimsonX

+0

कारण 1.0/0.1 बाहर आता है "दाएं" क्योंकि 1.0/.0999 ... और 1.0/0.1 लगभग बराबर हैं - समारोह निरंतर है। गणना कुछ अतिरिक्त बिट्स के साथ की जाती है, अंतिम प्रतिनिधित्व योग्य परिणाम दोनों मामलों में समान होता है। इसके बाद यह 1.0/7.3_0 के उत्तर की उम्मीद के आधार पर "दाएं" दिखता है। लेकिन याद रखें कि मशीन को प्रस्तुत की गई समस्या 1.0% 0.1 नहीं थी, लेकिन 1.0% 0.10000000001 या कुछ और यह गणना वास्तव में परिणामस्वरूप होती है बड़ा शेष जो गोल नहीं किया जा सकता है। – DigitalRoss

2

तथ्य यह है कि 0.1 बाइनरी में बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है इसके साथ सब कुछ करने के लिए है।

यदि 0.1 को double के रूप में प्रदर्शित किया जा सकता है, तो आप जिस ऑपरेशन की गणना करना चाहते हैं उसके वास्तविक परिणाम के लिए आपको प्रतिनिधित्व करने योग्य डबल निकटतम (निकटतम "निकटतम" राउंडिंग मोड) प्राप्त होगा।

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

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

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

0

त्रुटि आप देख छोटा है, यह केवल पहली नज़र में बड़ा दिखता है। आपका परिणाम (प्रदर्शन के लिए गोलाई के बाद) 0.09999999999999995 == (0.1 - 5e-17) जब आप एक % 0.1 आपरेशन से 0 की उम्मीद थी। लेकिन याद रखें कि यह लगभग 0.1 है, और 0.1 % 0.1 == 0 है।

तो आपकी वास्तविक त्रुटि यहां -5e-17 है। मैं यह छोटा कहूंगा।

क्या आप के लिए संख्या की आवश्यकता के आधार पर यह बेहतर हो सकता है लिखने के लिए:

double result = 1.0 % 0.1; result = result >= 0.1/2 ? result - 0.1 : result;

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^