2008-09-18 35 views
12

Hoạt động của điểm nổi C# /. NET có khác biệt chính xác giữa chế độ gỡ lỗi và chế độ phát hành không?Độ chính xác cao/kép ở chế độ gỡ lỗi/giải phóng

+0

Tại sao bạn cho rằng chúng khác nhau? –

+0

Vâng, tôi cũng muốn tìm hiểu quy trình suy nghĩ của bạn. –

+0

Câu hỏi đặt ra là về sự khác biệt giữa gỡ lỗi và phát hành. Bạn sẽ nghĩ rằng phiên bản phát hành có thể sử dụng các thanh ghi thay vì RAM sẽ có độ chính xác cao hơn: FPU = 80bit, double = 64bit, float = 32bit. – Skizz

Trả lời

21

Họ thực sự có thể khác nhau. Theo đặc tả CLR ECMA:

địa điểm lưu trữ cho dấu chấm động số (tĩnh học, các phần tử mảng, và lĩnh vực lớp) có kích thước cố định. Kích thước bộ nhớ được hỗ trợ là float32 và float64. Ở mọi nơi khác (trên ngăn xếp đánh giá, dưới dạng các đối số , dưới dạng các loại trả về và biến cục bộ) số được thể hiện bằng cách sử dụng loại dấu phẩy động bên trong . Trong mỗi ví dụ , loại danh nghĩa của biến hoặc biểu thức hoặc là R4 hoặc R8, nhưng giá trị của nó có thể được biểu thị nội bộ với phạm vi bổ sung và/hoặc độ chính xác. Kích thước của biểu diễn dấu phẩy động bên trong phụ thuộc vào việc triển khai thực hiện, có thể thay đổi, và phải có độ chính xác ít nhất là tuyệt vời như biểu thức hoặc biểu thức . An chuyển đổi mở rộng tiềm ẩn sang biểu diễn nội bộ từ float32 hoặc float64 được thực hiện khi các loại này được tải từ bộ nhớ. Các đại diện nội bộ thường là kích thước gốc cho phần cứng, hoặc theo yêu cầu để thực hiện một hoạt động hiệu quả .

Điều này về cơ bản có nghĩa là sự so sánh sau đây có thể hoặc có thể không bằng nhau:

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?). 
    } 
    } 
} 

nhắn Take-nhà: không bao giờ kiểm tra các giá trị nổi cho sự bình đẳng.

+0

Điều đó không liên quan gì đến DEBUG vs RELEASE xây dựng cấu hình mặc dù ... –

+2

IL được tạo ra sẽ giống nhau ... nhưng JITter ít hung hăng hơn khi giao dịch với một assembly được đánh dấu là debug. Bản phát hành bản phát hành có xu hướng di chuyển nhiều giá trị nổi hơn vào thanh ghi 80 bit; gỡ lỗi xây dựng có xu hướng đọc trực tiếp từ bộ nhớ 64-bit lưu trữ. – stusmith

+0

IL được tạo ra có thể không giống nhau. chế độ gỡ lỗi chèn nops ở những nơi để đảm bảo một breakpoint là có thể, nó cũng có thể cố tình duy trì một biến tạm thời mà chế độ phát hành có vẻ không cần thiết. – ShuggyCoUk

2

Thực tế, chúng có thể khác nếu chế độ gỡ lỗi sử dụng FPU x87 và chế độ phát hành sử dụng SSE cho float-op.

+1

Bạn có tài liệu tham khảo hoặc trình diễn có thẩm quyền không? –

0

Để đối phó với yêu cầu Frank Krueger của trên (trong ý kiến) cho một cuộc biểu tình của một sự khác biệt:

Biên dịch mã này trong gcc không có tối ưu hóa và -mfpmath = 387 (Tôi không có lý do gì để nghĩ rằng nó sẽ không làm việc trên các trình biên dịch khác, nhưng tôi đã không thử nó.) Sau đó biên dịch nó không có tối ưu hóa và -msse -mfpmath = sse.

Kết quả sẽ khác nhau.

#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âu hỏi là về C# /. Net; Ví dụ của bạn là cho C++/mã gốc. –

+1

Bằng cách nào đó tôi nghi ngờ rằng độ chính xác của SSE vs x87 FPU khác nhau tùy thuộc vào ngôn ngữ bạn gọi nó từ! –

+0

Một bản dịch trực tiếp sang C# cũng hiển thị các kết quả khác nhau cho x86 và x64 trong Visual Studio 2013. Lưu ý rằng x86 CLR sử dụng x87 FPU trong khi x64 CLR sử dụng SSE. – Asik

11

Đây là một câu hỏi thú vị, vì vậy tôi đã làm một chút thử nghiệm.Tôi đã sử dụng mã này:

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"); 
    } 
} 

sử dụng DevStudio 2005 và Net 2. Tôi biên soạn như cả hai gỡ lỗi và phát hành và kiểm tra đầu ra của trình biên dịch:

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    
                   } 

gì các chương trình trên là sự nổi mã điểm là như nhau cho cả hai gỡ lỗi và phát hành, trình biên dịch được lựa chọn nhất quán hơn tối ưu hóa. Mặc dù chương trình tạo ra kết quả sai (a * a không nhỏ hơn b) nó giống nhau bất kể chế độ gỡ lỗi/phát hành.

Bây giờ, FPU Intel IA32 có 8 thanh ghi dấu chấm động, bạn sẽ nghĩ trình biên dịch sẽ sử dụng thanh ghi để lưu trữ giá trị khi tối ưu hóa thay vì ghi vào bộ nhớ, do đó cải thiện hiệu suất, điều gì đó dọc theo các dòng:

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 

nhưng điều này sẽ thực thi khác với phiên bản gỡ lỗi (trong trường hợp này, hiển thị kết quả chính xác). Tuy nhiên, thay đổi hành vi của chương trình tùy thuộc vào các tùy chọn xây dựng là một điều rất xấu.

Mã FPU là một lĩnh vực mà việc tạo thủ công mã có thể thực hiện tốt hơn đáng kể trình biên dịch, nhưng bạn cần phải thu hút đầu của bạn xung quanh cách FPU hoạt động.

Các vấn đề liên quan