2013-02-06 36 views
5

जा रहा मैं पर्लपर्ल बिगड़

for ($i=0; $i <= 360; $i += 0.01) 
{ 
print "$i "; 
} 

क्यों कि जब मैं इस कोड को मैं निम्नलिखित आउटपुट प्राप्त चलाते हैं, जहां जैसे ही यह 0.81 करने के लिए हो जाता है यह अचानक शुरू होता है यह है में पाश के लिए एक सरल है एक लोड में अधिक दशमलव स्थानों में जोड़ें? मुझे पता है कि मैं इस मुद्दे से बचने के लिए बस गोल कर सकता था लेकिन मैं सोच रहा था कि ऐसा क्यों होता है। 0.01 की वृद्धि, पागल करने के लिए बिल्कुल पागल नहीं लगती है।

0.77 
0.78 
0.79 
0.8 
0.81 
0.820000000000001 
0.830000000000001 
0.840000000000001 
0.850000000000001 
0.860000000000001 
0.870000000000001 
+2

http://learn.perl.org/faq/perlfaq4.html#Why-am-I-getting-long-decimals-eg-19.9499999999999-instead-of-the- संख्या-मैं-होना-होना-eg-19.95- – mob

+0

http://stackoverflow.com/questions/10908825/, http://stackoverflow.com/questions/9790048, http://stackoverflow.com/questions/ 3 9 16314, http://stackoverflow.com/questions/7066636, http://stackoverflow.com/questions/2080629, http://stackoverflow.com/questions/2056681, http://stackoverflow.com/questions/14204125 – mob

+0

8.1 रूपांतरित करने के लिए एक उल्लेखनीय हार्ड संख्या प्रतीत होता है। सालों पहले, मेरे पास एक मिनी-प्रोजेक्ट था जो किसी विशेष मूल्य में हेरफेर करने के लिए * कुछ * तरीका खोजने की कोशिश कर रहा था: '8.10' सटीक रूप से। जावा और पर्ल दोनों में, इस नंबर के साथ समस्याएं थीं। – Axeman

उत्तर

15

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

messed up recepit http://img.thedailywtf.com/images/200902/errord/DSC00669.JPG

(चित्र dailywtf से लिया)

सबसे सुरुचिपूर्ण गणना के लिए पूर्णांकों उपयोग कर रहा है इस समस्या के समाधान प्राप्त करने के लिए जिस तरह से, उन्हें दशमलव की सही संख्या को विभाजित: यह एक ही कारण why you shouldn't use floating point numbers for monetary values है मुद्रित दशमलव स्थानों की संख्या सीमित करने के लिए स्थानों और sprintf का उपयोग कर।यह दोनों के लिए सुनिश्चित कर देगा:

#!/usr/bin/perl 
for ($i=0; $i <= 360*100; $i += 1) { 
    printf "%.2f \n", $i/100; 
} 
+7

जंगली में गलत प्रोग्रामिंग प्रथाओं के वृत्तचित्र साक्ष्य के लिए +1! –

7

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

यह (बाइनरी) फ्लोटिंग पॉइंट अंकगणित की मूलभूत संपत्ति है और पर्ल के लिए विशिष्ट नहीं है। What Every Computer Scientist Should Know About Floating-Point Arithmetic मानक संदर्भ है, और आप इसे Google खोज के साथ बहुत आसानी से पा सकते हैं।

यह भी देखें: C compiler bug (floating point arithmetic) और इसमें कोई संदेह नहीं है कि असंख्य अन्य प्रश्न हैं।


Kernighan & Plauger कहते हैं, अपने पुराने लेकिन क्लासिक किताब "The Elements of Programming Style" में, कि:

  • एक बुद्धिमान पुराने प्रोग्रामर बार कहा था "चल बिन्दु संख्या रेत के छोटे से बवासीर की तरह कर रहे हैं; हर बार जब आप एक के लिए कदम , आप थोड़ा रेत खो देते हैं और थोड़ा गंदगी प्राप्त करते हैं "।

उन्होंने यह भी कहते हैं:

  • 10 * 0,1 शायद ही कभी 1,0

दोनों बातें कहना है कि चल बिन्दु गणित सटीक नहीं है।

ध्यान दें कि कुछ आधुनिक CPUs (आईबीएम पावरपीसी) में आईईईई 754: 2008 दशमलव फ़्लोटिंग पॉइंट अंकगणित अंतर्निहित है। यदि पर्ल सही प्रकारों का उपयोग करता है (यह शायद नहीं करता है), तो आपकी गणना सटीक होगी।

2

1/10 आवधिक है:

  • परिणाम मुद्रित
  • गोलाई त्रुटि जमा नहीं करता

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

>perl -E"say sprintf '%.17f', 0.1" 
0.10000000000000001 

या तो पूर्णांकों

for (0*100..360*100) { 
    my $i = $_/100; 
    print "$i "; 
} 

या

for (my $i=0; $i <= 360; $i = sprintf('%.2f', $i + 0.01)) { 
    print "$i "; 
} 
4

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

सी में लूप:

#include <stdio.h> 

    int main() { 
     double i; 
     for(i=0;i<=1;i+=.01) { 
      printf("%.15f\n",i); 
     } 
     } 

आउटपुट:

0.790000000000000 
    0.800000000000000 
    0.810000000000000 
    0.820000000000001 
    0.830000000000001 
    0.840000000000001 
    0.850000000000001 

बिंदु आगे भी सी में पाश प्रदर्शित करने के लिए, कोड, लेकिन अब चल बिन्दु अंकगणितीय एकल परिशुद्धता का उपयोग करें और देखें कि उत्पादन कम सटीक और यहां तक ​​कि अधिक अनियमित है।

आउटपुट:

0.000000000000000 
    0.009999999776483 
    0.019999999552965 
    0.029999999329448 
    0.039999999105930 
    0.050000000745058 
    0.060000002384186 
    0.070000000298023 
    0.079999998211861 
    0.089999996125698 
    0.099999994039536