2008-09-18 8 views
12

C#/.NET 부동 소수점 연산의 디버그 모드와 릴리스 모드의 정밀도는 서로 다릅니 까?디버그/릴리스 모드의 부동 소수점/이중 정밀도

+0

왜 다른가? –

+0

네, 당신의 사고 과정을 찾는 것에 관심이 있습니다. –

+0

질문은 디버그와 릴리스의 차이점에 대한 것입니다. 릴리스 버전은 RAM보다 정밀도가 높은 레지스터 인 FPU = 80bit, double = 64bit, float = 32bit를 사용한다고 생각할 것입니다. – Skizz

답변

21

그들은 참으로 다를 수있는 다른 것입니다 무슨 짓을했는지에 말하는 기사의 몇 가지를 발견했다. CLR은 ECMA 사양에 따르면, 부동 소수점 숫자

저장 위치 (정적, 배열 요소 및 클래스 필드 하는) 고정 된 크기이다. 지원되는 저장 장치 크기는 float32 및 float64입니다. 다른 곳에서는 (평가 스택에서는 인수로 반환 유형으로, 로컬 변수로) 부동 소수점 숫자는 내부 부동 소수점 유형을 사용하여 나타냅니다. 이러한 경우 변수 또는 표현식의 공칭 유형은 R4 또는 R8이지만 그 값은 내부적으로 및/또는 정밀도가있는 으로 표시 될 수 있습니다. 내부 부동 소수점 표현 의 크기는 구현에 따라 다르며 과 다를 수 있으며 변수의 값이 인 표현식이 표시 될 때 최소한 만큼 큰 정밀도를 가져야합니다. float32 또는 float64에서 내부 표현 내부 변환으로의 암시 적 확대 변환이 수행됩니다. 유형이 저장소에서로드됩니다. 내부 표현은 일반적으로 하드웨어의 고유 크기 인 이거나 효율적인 구현을 위해 필요한 경우 입니다.

class Foo 
{ 
    double _v = ...; 

    void Bar() 
    { 
    double v = _v; 

    if(v == _v) 
    { 
     // Code may or may not execute here. 
     // _v is 64-bit. 
     // v could be either 64-bit (debug) or 80-bit (release) or something else (future?). 
    } 
    } 
} 

테이크 가정 메시지 :이 기본적으로 의미

는 다음 비교거나 같아야하지 않을 수 있다는 것이다 평등을위한 부동 값을 확인하지 않습니다.

+0

DEBUG vs RELEASE 빌드 구성과 아무 관계가 없습니다 ... –

+2

생성 된 IL은 같지만 ... 디버그로 표시된 어셈블리를 다룰 때 JITter는 덜 공격적입니다. 릴리스 빌드는 더 많은 부동 값을 80 비트 레지스터로 이동시키는 경향이 있습니다. 디버그 빌드는 64 비트 메모리 저장소에서 직접 읽는 경향이 있습니다. – stusmith

+0

생성 된 IL이 같지 않을 수 있습니다. 디버그 모드는 중단 점이 가능한지 확인하기 위해 nop를 삽입합니다. 또한 해제 모드가 불필요하다고 판단하는 임시 변수를 의도적으로 유지 관리 할 수도 있습니다. – ShuggyCoUk

2

디버그 모드에서 x87 FPU를 사용하고 릴리스 모드에서 float-ops에 SSE를 사용하면 실제로 다를 수 있습니다.

더 최적화를 GCC에이 코드를 컴파일하고 -mfpmath = 387 (내가 생각하는 이유가 없을 것이다 없습니다 : 차이의 시연 (주석) 위의 프랭크 크루거의 요청에 대한 응답으로

+1

신뢰할만한 참조 또는 데모가 있습니까? –

0

다른 컴파일러에서 작동하지만 시도하지는 않았습니다.) 그런 다음 최적화없이 -msse -mfpmath = sse로 컴파일하십시오.

출력이 다를 수 있습니다.

#include <stdio.h> 

int main() 
{ 
    float e = 0.000000001; 
    float f[3] = {33810340466158.90625,276553805316035.1875,10413022032824338432.0}; 
    f[0] = pow(f[0],2-e); f[1] = pow(f[1],2+e); f[2] = pow(f[2],-2-e); 
    printf("%s\n",f); 
    return 0; 
} 
+1

질문은 C#/.Net에 관한 것이 었습니다. 귀하의 예제는 C + +/네이티브 코드입니다. –

+1

어쨌든 SSE 대 x87 FPU의 정밀도가 당신이 부르는 언어에 따라 다르다는 것을 나는 의심한다! –

+0

C#으로 직접 변환하면 Visual Studio 2013에서 x86 및 x64에 대해 다른 결과가 나타납니다. x86 CLR은 x87 FPU를 사용하고 x64 CLR은 SSE를 사용합니다. – Asik

11

이것은 흥미로운 질문입니다. 그래서 나는 약간의 실험을했습니다.모두 디버그로 컴파일 및 해제 DevStudio와 2005 년과 닷넷 2.를 사용하여

static void Main (string [] args) 
{ 
    float 
    a = float.MaxValue/3.0f, 
    b = a * a; 

    if (a * a < b) 
    { 
    Console.WriteLine ("Less"); 
    } 
    else 
    { 
    Console.WriteLine ("GreaterEqual"); 
    } 
} 

및 컴파일러의 출력 검사 :이 코드를 사용

Release             Debug 

    static void Main (string [] args)      static void Main (string [] args) 
    {              { 
                 00000000 push  ebp 
                 00000001 mov   ebp,esp 
                 00000003 push  edi 
                 00000004 push  esi 
                 00000005 push  ebx 
                 00000006 sub   esp,3Ch 
                 00000009 xor   eax,eax 
                 0000000b mov   dword ptr [ebp-10h],eax 
                 0000000e xor   eax,eax 
                 00000010 mov   dword ptr [ebp-1Ch],eax 
                 00000013 mov   dword ptr [ebp-3Ch],ecx 
                 00000016 cmp   dword ptr ds:[00A2853Ch],0 
                 0000001d je   00000024 
                 0000001f call  793B716F 
                 00000024 fldz    
                 00000026 fstp  dword ptr [ebp-40h] 
                 00000029 fldz    
                 0000002b fstp  dword ptr [ebp-44h] 
                 0000002e xor   esi,esi 
                 00000030 nop    
     float              float 
     a = float.MaxValue/3.0f,        a = float.MaxValue/3.0f, 
00000000 sub   esp,0Ch       00000031 mov   dword ptr [ebp-40h],7EAAAAAAh 
00000003 mov   dword ptr [esp],ecx     
00000006 cmp   dword ptr ds:[00A2853Ch],0   
0000000d je   00000014        
0000000f call  793B716F        
00000014 fldz            
00000016 fstp  dword ptr [esp+4]      
0000001a fldz            
0000001c fstp  dword ptr [esp+8]      
00000020 mov   dword ptr [esp+4],7EAAAAAAh   
     b = a * a;            b = a * a; 
00000028 fld   dword ptr [esp+4]     00000038 fld   dword ptr [ebp-40h] 
0000002c fmul  st,st(0)       0000003b fmul  st,st(0) 
0000002e fstp  dword ptr [esp+8]     0000003d fstp  dword ptr [ebp-44h] 

     if (a * a < b)           if (a * a < b) 
00000032 fld   dword ptr [esp+4]     00000040 fld   dword ptr [ebp-40h] 
00000036 fmul  st,st(0)       00000043 fmul  st,st(0) 
00000038 fld   dword ptr [esp+8]     00000045 fld   dword ptr [ebp-44h] 
0000003c fcomip  st,st(1)       00000048 fcomip  st,st(1) 
0000003e fstp  st(0)        0000004a fstp  st(0) 
00000040 jp   00000054       0000004c jp   00000052 
00000042 jbe   00000054       0000004e ja   00000056 
                 00000050 jmp   00000052 
                 00000052 xor   eax,eax 
                 00000054 jmp   0000005B 
                 00000056 mov   eax,1 
                 0000005b test  eax,eax 
                 0000005d sete  al 
                 00000060 movzx  eax,al 
                 00000063 mov   esi,eax 
                 00000065 test  esi,esi 
                 00000067 jne   0000007A 
     {               { 
     Console.WriteLine ("Less");      00000069 nop    
00000044 mov   ecx,dword ptr ds:[0239307Ch]    Console.WriteLine ("Less"); 
0000004a call  78678B7C       0000006a mov   ecx,dword ptr ds:[0239307Ch] 
0000004f nop           00000070 call  78678B7C 
00000050 add   esp,0Ch       00000075 nop    
00000053 ret             } 
     }             00000076 nop    
     else            00000077 nop    
     {             00000078 jmp   00000088 
     Console.WriteLine ("GreaterEqual");      else 
00000054 mov   ecx,dword ptr ds:[02393080h]    { 
0000005a call  78678B7C       0000007a nop    
     }               Console.WriteLine ("GreaterEqual"); 
    }             0000007b mov   ecx,dword ptr ds:[02393080h] 
                 00000081 call  78678B7C 
                 00000086 nop    
                   } 

위의 쇼가 떠있는 것을 무엇을 포인트 코드는 디버그와 릴리스에서 모두 동일하므로 컴파일러는 최적화보다 일관성을 선택합니다. 프로그램이 잘못된 결과를 산출하지만 (a * a는 b보다 작지 않음) 디버그/릴리스 모드에 관계없이 동일합니다.

이제 인텔 IA32 FPU 여덟 개 부동 소수점 레지스터를 가지고, 당신은 최적화가 아닌 메모리에 기록하는, 따라서의 라인을 따라 뭔가를 성능을 개선 할 때 컴파일러가 값을 저장 레지스터를 사용하는 것이라고 생각 :

fld   dword ptr [a] ; precomputed value stored in ram == float.MaxValue/3.0f 
fmul  st,st(0) ; b = a * a 
; no store to ram, keep b in FPU 
fld   dword ptr [a] 
fmul  st,st(0) 
fcomi  st,st(0) ; a*a compared to b 

하지만 디버그 버전과 다르게 실행됩니다 (이 경우 올바른 결과 표시). 그러나 빌드 옵션에 따라 프로그램 동작을 변경하는 것은 매우 나쁜 일입니다.

FPU 코드는 코드 작성자가 컴파일러를 능가 할 수있는 영역 중 하나이지만 FPU 작동 방식에 대해 머리를 숙여 야합니다.

관련 문제