2009-04-15 13 views
12

इस स्पष्ट कलाकार का परिणाम निहित से अलग क्यों है?इस स्पष्ट कलाकार का परिणाम निहित से अलग क्यों है?

#include <stdio.h> 

double a; 
double b; 
double c; 

long d; 

double e; 

int main() { 
    a = 1.0; 
    b = 2.0; 
    c = .1; 

    d = (b - a + c)/c; 
    printf("%li\n", d);  // 10 

    e = (b - a + c)/c; 
    d = (long) e; 
    printf("%li\n", d);  // 11 
    } 

अगर मैं d = (लंबी) कर ((ख - एक + स)/सी); मुझे भी 10 मिलते हैं। डबल के लिए असाइनमेंट क्यों फर्क पड़ता है?

+0

वे मेरे सिस्टम पर समान (दोनों 11) हैं? –

+0

आप इसे किस साथ संकलित कर रहे हैं? – Joseph

+0

बस मज़े के लिए, एक स्थानीय चर बनाने की कोशिश करें और देखें कि क्या चीजें बदलती हैं। –

उत्तर

16

मुझे लगता अंतर एक 64-बिट एक और तो एक एक लंबे करने के लिए रूपांतरण के लिए एक लंबे एक 80-बिट चल बिन्दु मान से एक रूपांतरण बनाम करने के लिए एक 80-बिट चल बिन्दु मान से एक रूपांतरण है।

(80 बिट बिल्कुल भी आने के लिए कारण यह है कि कि एक विशिष्ट वास्तविक अंकगणित के लिए इस्तेमाल किया सटीक, और चल बिन्दु रजिस्टरों की चौड़ाई है।)

मान लीजिए 80-बिट परिणाम 10.999999999999999 की तरह कुछ है - कि से एक लंबी पैदावार से 10 हालांकि करने के लिए रूपांतरण, 80-बिट मूल्य के सबसे नजदीक 64-बिट चल बिन्दु मूल्य वास्तव में 11.0 है, इसलिए दो चरणों रूपांतरण उपज 11.

संपादित समाप्त होता है: यह एक देने के लिए थोड़ा अधिक वजन ...

यहां एक जावा प्रोग्राम है जो सैम करने के लिए मनमाना-परिशुद्धता अंकगणित का उपयोग करता है ई गणना। ध्यान दें कि यह डबल मान को 0.1 से एक बिगडिसीमल में परिवर्तित करता है - यह मान 0.1000000000000000055511151231257827021181583404541015625 है। (दूसरे शब्दों में, गणना की सटीक परिणाम नहीं 11 वैसे भी है।)

import java.math.*; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     BigDecimal c = new BigDecimal(0.1d);   
     BigDecimal a = new BigDecimal(1d); 
     BigDecimal b = new BigDecimal(2d); 

     BigDecimal result = b.subtract(a) 
          .add(c) 
          .divide(c, 40, RoundingMode.FLOOR); 
     System.out.println(result); 
    } 
} 

यहाँ परिणाम है:

10.9999999999999994448884876874217606030632 

दूसरे शब्दों में, यह सही बारे में 40 दशमलव अंक के लिए (रास्ता है 64 या 80 बिट फ्लोटिंग पॉइंट से अधिक संभाल सकता है)।

अब, मान लीजिए कि यह संख्या बाइनरी में कैसा दिखती है। मेरे पास आसानी से रूपांतरण करने के लिए कोई उपकरण नहीं है, लेकिन फिर हम जावा का उपयोग करने के लिए उपयोग कर सकते हैं। एक सामान्यीकृत संख्या मान लिया जाये कि, "10" भाग तीन बिट्स (एक ग्यारह = 1011 के लिए कम से कम) का उपयोग कर समाप्त होता है। इससे विस्तारित परिशुद्धता (80 बिट्स) और 48 बिट्स डबल परिशुद्धता (64 बिट्स) के लिए मंटिसा के 60 बिट छोड़ देता है।

तो, प्रत्येक परिशुद्धता में 11 के लिए निकटतम संख्या क्या है?

import java.math.*; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     BigDecimal half = new BigDecimal("0.5");   
     BigDecimal eleven = new BigDecimal(11); 

     System.out.println(eleven.subtract(half.pow(60))); 
     System.out.println(eleven.subtract(half.pow(48)));   
    } 
} 

परिणाम::

10.999999999999999999132638262011596452794037759304046630859375 
10.999999999999996447286321199499070644378662109375 

तो, तीन नंबर हम मिल गया है कर रहे हैं:

Correct value: 10.999999999999999444888487687421760603063... 
11-2^(-60): 10.999999999999999999132638262011596452794037759304046630859375 
11-2^(-48): 10.999999999999996447286321199499070644378662109375 

अब सही के लिए निकटतम मूल्य बाहर काम फिर से, के जावा का उपयोग करते हैं प्रत्येक परिशुद्धता के लिए - विस्तारित परिशुद्धता के लिए, यह 11 से कम है। उन मानों में से प्रत्येक को लंबे समय तक गोल करें, और आप क्रमश: 10 और 11 के साथ समाप्त होते हैं।

उम्मीद है कि यह पर्याप्त सबूत संदेह करने वालों को समझाने के लिए है;)

+0

यह एक शिक्षित अनुमान है जो सी # में समान प्रभाव देखता है। यह प्रोसेसर और कंपाइलर-निर्भर बीटीडब्ल्यू होगा। क्या मैं 100% यकीन करता हूं कि यह क्या हो रहा है? नहीं। क्या मुझे लगता है कि यह एक बहुत ही संभावित स्पष्टीकरण है? पूर्ण रूप से। आईएमओ "मेरी मशीन पर काम करता है" से अधिक उपयोगी। –

+0

http://babbage.cs.qc.edu/IEEE-754/ इस तरह की चीज़ के लिए बहुत उपयोगी है, हालांकि इसमें केवल 32- और 64-बिट कैलकुलेटर हैं, न कि 80-बिट कैलकुलेटर। –

+0

@ एडम: लिंक के लिए बहुत बहुत धन्यवाद। वास्तव में उपयोगी यह उपयोगी होगा अगर अंतिम "दशमलव" मान * सटीक * मान था जो निकटतम डबल द्वारा दर्शाया गया था। –

0

सीधे कॉपी/पेस्ट और लिनक्स पर संकलन मुझे दोनों के लिए 11 देता है। d = (long) ((b - a + c)/c); जोड़ने से भी 11 ही OpenBSD पर चला जाता है देता है।

+0

ओएस मामले की संभावना नहीं है। कंपाइलर + विकल्प + प्रोसेसर कहीं अधिक प्रासंगिक हैं। –

1

codepad.org (gcc 4.1।2) आपके स्थानीय सिस्टम (जीसीसी 4.3.2) पर आपके उदाहरण के परिणामों को उलट देता है, मुझे दोनों मामलों में 11 मिलते हैं। यह मुझे बताता है कि यह एक फ्लोटिंग प्वाइंट मुद्दा है। वैकल्पिक रूप से, यह सैद्धांतिक रूप से छिड़काव कर सकता है (बी - ए + सी) जो, एक पूर्णांक संदर्भ में (2 - 1 + 0)/.1 का मूल्यांकन करेगा, जो 10 होगा, जबकि एक फ्लोट संदर्भ में (2.0 - 1.0 + 0.1)/.1 = 1.1/.1 = 11. हालांकि यह अजीब होगा।

+0

सी का मान 0.1 से शुरू नहीं है। यह सिर्फ 0.1 के करीब निकटतम है। –

0

Here is a bunch of detail on floating point issues and a really good article. लेकिन मूल रूप से, सभी फ़्लोटिंग पॉइंट मानों को बिट्स (32-बिट्स या 64-बिट्स या जो कुछ भी) द्वारा प्रदर्शित नहीं किया जा सकता है। यह एक गहरी विषय है, लेकिन मुझे पसंद है क्योंकि यह मुझे Prof. Kahan की याद दिलाता है। :)

2

मुझे अपने 32-बिट x86 लिनक्स सिस्टम पर gcc 4.3.2 चलने पर 10 & 11 मिलता है।

प्रासंगिक सी/एएसएम यहाँ है:

26:foo.c   ****  d = (b - a + c)/c;            
    42       .loc 1 26 0 
    43 0031 DD050000    fldl b 
    43  0000 
    44 0037 DD050000    fldl a 
    44  0000 
    45 003d DEE9     fsubrp %st, %st(1) 
    46 003f DD050000    fldl c 
    46  0000 
    47 0045 DEC1     faddp %st, %st(1) 
    48 0047 DD050000    fldl c 
    48  0000 
    49 004d DEF9     fdivrp %st, %st(1) 
    50 004f D97DFA    fnstcw -6(%ebp) 
    51 0052 0FB745FA    movzwl -6(%ebp), %eax 
    52 0056 B40C     movb $12, %ah 
    53 0058 668945F8    movw %ax, -8(%ebp) 
    54 005c D96DF8    fldcw -8(%ebp) 
    55 005f DB5DF4    fistpl -12(%ebp) 
    56 0062 D96DFA    fldcw -6(%ebp) 
    57 0065 8B45F4    movl -12(%ebp), %eax 
    58 0068 A3000000    movl %eax, d 
    58  00 
    27:foo.c   **** 
    28:foo.c   ****  printf("%li\n", d);             
    59       .loc 1 28 0 
    60 006d A1000000    movl d, %eax 
    60  00 
    61 0072 89442404    movl %eax, 4(%esp) 
    62 0076 C7042400    movl $.LC3, (%esp) 
    62  000000 
    63 007d E8FCFFFF    call printf 
    63  FF 
    29:foo.c   ****  // 10               
    30:foo.c   **** 
    31:foo.c   ****  e = (b - a + c)/c;            
    64       .loc 1 31 0 
    65 0082 DD050000    fldl b 
    65  0000 
    66 0088 DD050000    fldl a 
    66  0000 
    67 008e DEE9     fsubrp %st, %st(1) 
    68 0090 DD050000    fldl c 
    68  0000 
    69 0096 DEC1     faddp %st, %st(1) 
    70 0098 DD050000    fldl c 
    70  0000 
    71 009e DEF9     fdivrp %st, %st(1) 
    72 00a0 DD1D0000    fstpl e 
    72  0000 
    32:foo.c   **** 
    33:foo.c   ****  d = (long) e;              
    73       .loc 1 33 0 
    74 00a6 DD050000    fldl e 
    74  0000 
    75 00ac D97DFA    fnstcw -6(%ebp) 
    76 00af 0FB745FA    movzwl -6(%ebp), %eax 
    77 00b3 B40C     movb $12, %ah 
    78 00b5 668945F8    movw %ax, -8(%ebp) 
    79 00b9 D96DF8    fldcw -8(%ebp) 
    80 00bc DB5DF4    fistpl -12(%ebp) 
    81 00bf D96DFA    fldcw -6(%ebp) 
    82 00c2 8B45F4    movl -12(%ebp), %eax 
    83 00c5 A3000000    movl %eax, d 
    83  00 

जवाब रुचि पाठक के लिए एक व्यायाम के रूप में छोड़ दिया जाता है।

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