2017-11-30 27 views
6

이 코드를 살펴보십시오. 10/3 return 3.333333 604736328125000 그리고 제가 계산기로 3을 곱하면 저는 9.99가됩니다. 그러나 코드로 똑같이하면 정확히 10.00이됩니다. 어떨까요?왜 C에서 10/3인지 정확히 알겠습니까?

0000000000400497 <main>: 
    400497:  55      push rbp 
    400498:  48 89 e5    mov rbp,rsp 
    40049b:  f3 0f 10 05 b1 00 00 movss xmm0,DWORD PTR [rip+0xb1]  # 400554 <_IO_stdin_used+0x4> 
    4004a2:  00 
    4004a3:  f3 0f 11 45 fc   movss DWORD PTR [rbp-0x4],xmm0 
    4004a8:  f3 0f 10 4d fc   movss xmm1,DWORD PTR [rbp-0x4] 
    4004ad:  f3 0f 10 05 a3 00 00 movss xmm0,DWORD PTR [rip+0xa3]  # 400558 <_IO_stdin_used+0x8> 
    4004b4:  00 
    4004b5:  f3 0f 59 c1    mulss xmm0,xmm1 
    4004b9:  f3 0f 11 45 f8   movss DWORD PTR [rbp-0x8],xmm0 
    4004be:  b8 00 00 00 00   mov eax,0x0 
    4004c3:  5d      pop rbp 
    4004c4:  c3      ret  
    4004c5:  66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 
    4004cc:  00 00 00 
    4004cf:  90      nop 

내가 mulss가 CPU 기능에 의해 반올림 생각 :

#include <stdlib.h> 
#include <stdio.h> 

int main() { 

    float v = 10.f/3.f; 
    float test = v*3.f; 
    printf("10/3 => %25.25f \n (10/3)*3 => %25.25f\n",v,test); 
    return 0; 
} 

이 기본 GCC에게 7.2.1 매개 변수를 사용하여 컴파일의 printf없이 어셈블리 코드이다. BC는 GNU 프로그램 참고

, 10/3은 3.3333333333333333333333는 (* 3 => 9.9999) 및 SciLab으로는 4813 631 3.333333333333333 (* 3 => 10)을 리턴.

+0

설명은 확장 토론이 아닙니다. 이 대화는 [채팅으로 이동되었습니다] (http://chat.stackoverflow.com/rooms/160240/discussion-on-question-by-amanda-osvaldo-why-10-3-its-exact-in-c) . – Andy

+1

'mulss' 결과는 가장 가까운 것으로 반올림되는 기본 IEEE754 반올림 모드를 사용하여 반올림됩니다. (프로그램은'#pragma FENV_ACCESS ON'을 사용하지 않고 FP 반올림 모드를 다른 것으로 설정하기 때문에 컴파일러는 기본 반올림 모드를 사용하는 프로그램을 생성합니다.) –

답변

8

표현이 제대로 작동하기 때문에 결과적으로 정확히 10이됩니다. float과 double에 대한 구현에서 동일한 결과를 얻습니다.

의 두 번 사용하는 예를 살펴 보자 :

0x1.aaaaaaaaaaaabp+1 

이는 IEEE754 이중 표현 0x401aaaaaaaaaaaab와 일치합니다

우리가 %a를 사용하여 점 표기법을 떠 진수 10./3.을 인쇄 할 경우, 우리는이 얻을.

정상화 위의 번호는 : 바이너리에서

0x3.5555555555558 

:

11.0101010101010101010101010101010101010101010101011 

는의 대신에 3을 곱하여의 세 번 추가 할 수 있도록, 간단하게하기 위해 :

 11.0101010101010101010101010101010101010101010101011 
+ 11.0101010101010101010101010101010101010101010101011 
--------------------------------------------------------- 
    110.1010101010101010101010101010101010101010101010111 
+ 11.0101010101010101010101010101010101010101010101011 
--------------------------------------------------------- 
    1010.0000000000000000000000000000000000000000000000000 

을 어느 정확히 10입니다.

편집 :

나는 마지막 몇 자리 숫자를 계산할 수있을 것 같습니다. 실제 합계 :

 11.0101010101010101010101010101010101010101010101011 
+ 11.0101010101010101010101010101010101010101010101011 
--------------------------------------------------------- 
    110.1010101010101010101010101010101010101010101010110 
+ 11.0101010101010101010101010101010101010101010101011 
--------------------------------------------------------- 
    1010.0000000000000000000000000000000000000000000000001 

그래서 그렇지 않아 정확히 10 만, 최하위 비트에 의해 떨어져.

float를 사용할 때 비슷한 차이가 나타났습니다.

10.f/3.f%a 인쇄 :

0x1.aaaaaap+1 

정규화 :

0x3.555554 

진 :

11.0101010101010101010101 

그런 다음 우리가 추가

 11.0101010101010101010101 
+ 11.0101010101010101010101 
------------------------------ 
    110.1010101010101010101010 
+ 11.0101010101010101010101 
------------------------------ 
    1001.1111111111111111111111 

다시 한번 최하위 비트만큼 끕니다.

실제 결과가 어떻게 반올림되었는지는 말할 수 없습니다.

+0

첫 번째 추가가 올바르게 되었습니까? 맨 오른쪽 비트는 1로, 여기에 표시된대로 '1'대신 '1 + 1'이 '10'과 같지 않아야합니까? 그리고 그 추가는 가장 오른쪽에 어딘가에서 길잃은'1 '을 남겨 두어야한다고 생각합니다. 아마도 그때는 반올림 될 것입니다.하지만 ... – ilkkachu

+0

@ilkkachu : FP 곱셈은 끝날 때까지 반복되지 않으므로 추가로 다시 만들려는 시도는 여분의 비트를 유지해야합니다. 이것은 일반 바이너리 수학이 아닌 지수/가수 (분수 부분에서 정수를 분리하는 올바른 위치의 기점과 함께)가 아니라고 생각합니다. 나는 이것이 어지러운 것처럼 보인다고 생각하지만, 정확한 수정은 추가적인 정밀도를 유지하는 것이다. –

+1

@PeterCordes, 예, 끝에서 반올림한다는 의미였습니다. 결과는 정수 부분에서 더 중요한 비트를 가지므로 일부 비트는 소수 부분에서 반올림해야합니다 – ilkkachu

2

당신이 C에보고 당신이 SciLab에서 보는 것은 SciLab 사용할 것 같다 반면에 당신이 C에서 단일 -precision 부동 소수점 값 (float)를 사용하는 것입니다 것 사이의 차이에 대한 이유 더블 - 기본값은 정밀도 값 (double)입니다.

차이점 here을 볼 수 있습니다 (숫자에서 f 접미어 만 제거하고 float 대신 double 입력).