문제
하 C#/.NET 부동 소수점 연산에서 차이가 정밀도 사이에 디버깅 모드와 릴리스 모드가 있습니까?
해결책
그들은 실제로 다를 수 있습니다.에 따라 CLR ECMA 사양:
저장 위치에 대한 부동 소수점 숫자(정역학,배열 요소,그리고 필드 클래스)크기도 고정되어 있습니다.지원되는 스토리지 크기 float32 및 float64.다른 곳에서 (에서 평가택 인수,으로 돌아의 유형,그리고 지역 변수)부동 소수점 숫자를 표현하는 내장 부동 소수점 형식이어야 합니다.에서 각 이러한 인스턴스 명목상의 유형 변수 또는 표현은 하나 R4 나 R8 지만,그 값을 표현할 수 있는 내부적으로 추가적인 범위 및/또는 정밀도입니다.의 크기 내장 부동 소수점 representation 은 구현에 따라 다를 수 있습니다 하여 정밀도가 최소한 하는 변수의 또는 식 표현되고있다.인 암시적으로 확대 변환기 내부 표현에서 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?).
}
}
}
집에 가져갈 메시지:지 확인 부동 값에 대한 평등.
다른 팁
이것은 흥미로운 질문을 했어요 그래서 약간의 실험을.이 코드:
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");
}
}
사용 DevStudio2005.Net2.나는 컴파일한 모두로 디버깅하고 방출 및 검토 출력의 컴파일러:
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 is not less than b)그것은 관계없이 동일하는 디버그/릴리스 모드입니다.
지금,Intel IA32FPU 은 여덟 부동 소수점 레지스터는,당신은 생각하는 컴파일러를 사용하여 레지스터 값을 저장할 때 최적화보다는 메모리에 쓰고,따라서 성능을 향상,무언가의 라인을 따라:
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 작동합니다.
사실,그들은 다를 수 있습니다면 디버깅 모드를 사용하 x87FPU 및 릴리스 모드로 사용하여 SSE for float-ops.
에 대응하여 프랭크 크루거의 요청이 위에서(주석)유의한 차이가:
이 코드를 컴파일에서 gcc 가 없는 최적화-mfpmath=387(나는 이유가 없다 생각하고 그것은 작동하지 않을 것에서 다른 컴파일러,하지만은 이렇게 말했습니다.) 그런 다음 그것을 컴파일없이 최적화-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;
}
여기에 간단한 예제는 결과뿐만 아니라 다를 디버깅 모드와 릴리스 모드지만,이 방법으로는 그들이 그렇게 여부에 따라 사용 x86 또는 x84 플랫폼으로:
Single f1 = 0.00000000002f;
Single f2 = 1 / f1;
Double d = f2;
Console.WriteLine(d);
이 기록은 다음과 같은 결과를 생성합니다.
Debug Release
x86 49999998976 50000000199,7901
x64 49999998976 49999998976
빠른 모습에서 분해(디버그->Windows>분해 Visual Studio)을 제공하는 일부에 대한 힌트를 여기에 무슨 일이 일어나.X86 경우:
Debug Release
mov dword ptr [ebp-40h],2DAFEBFFh | mov dword ptr [ebp-4],2DAFEBFFh
fld dword ptr [ebp-40h] | fld dword ptr [ebp-4]
fld1 | fld1
fdivrp st(1),st | fdivrp st(1),st
fstp dword ptr [ebp-44h] |
fld dword ptr [ebp-44h] |
fstp qword ptr [ebp-4Ch] |
fld qword ptr [ebp-4Ch] |
sub esp,8 | sub esp,8
fstp qword ptr [esp] | fstp qword ptr [esp]
call 6B9783BC | call 6B9783BC
특히,우리는 우리 무리의 겉으로 중복되는"저장하는 값에서 부동 소수점에 등록한 메모리로 다시 메모리에서 부동점"에 최적화되었다 릴리스 모드에서.그러나,두 개의 지침
fstp dword ptr [ebp-44h]
fld dword ptr [ebp-44h]
는 충분한 값을 변경하에서 x87 등록에서+5.0000000199790138e+0010 를+4.9999998976000000e+0010 중 하나로 확인할 수 있습을 통해 단계별로 분해 및 조사하는 값의 관련 레지스터(디버그->Windows->등록한 다음 마우스 오른쪽 버튼을 클릭하고"확인 부동 소수점").
에 대한 이야기 64 은 격렬하게 다릅니다.우리는 여전히 같은 최적화를 제거하는 몇 가지 지침이지만,이번에 모든 것에 의존하여 SSE 그 128 비트 레지스터와 전용 지시 설정:
Debug Release
vmovss xmm0,dword ptr [7FF7D0E104F8h] | vmovss xmm0,dword ptr [7FF7D0E304C8h]
vmovss dword ptr [rbp+34h],xmm0 | vmovss dword ptr [rbp-4],xmm0
vmovss xmm0,dword ptr [7FF7D0E104FCh] | vmovss xmm0,dword ptr [7FF7D0E304CCh]
vdivss xmm0,xmm0,dword ptr [rbp+34h] | vdivss xmm0,xmm0,dword ptr [rbp-4]
vmovss dword ptr [rbp+30h],xmm0 |
vcvtss2sd xmm0,xmm0,dword ptr [rbp+30h] | vcvtss2sd xmm0,xmm0,xmm0
vmovsd qword ptr [rbp+28h],xmm0 |
vmovsd xmm0,qword ptr [rbp+28h] |
call 00007FF81C9343F0 | call 00007FF81C9343F0
기 때문에,여기에 SSE 단위 방지를 사용하여 높은 정밀도를 보다 정밀도 단 하나 내부적으로(잠 x87 단위 않습니다)우리는 우리와 끝까지"정밀도 단 하나-틱"의 결과 86 경우에 관계없이 최적화를 달성할 수 있습니다.실제로 중 하나를 찾(사용하도록 설정한 후 SSE 등록 Visual Studio 레지스터 개요)그 이후 vdivss
,XMM0 포함 0000000000000000-00000000513A43B7 는 정확하게 49999998976 에서 전입니다.
모두 차트에서 저를 연습합니다.게다가 보여주는 하나 결코 비교 평등의 부동 소수점 이 예제에서는 또한 없다는 것을 보여줍의 여전히 어셈블리에서 디버깅 높은 수준의 언어는 C#같은 순간을 부인합니다.