2010-03-17 37 views
11

Xét đoạn mã sau:C# - không phù hợp toán kết quả hoạt động trên 32-bit và 64-bit

double v1 = double.MaxValue; 
double r = Math.Sqrt(v1 * v1); 

r = double.MaxValue trên 32-bit máy r = Infinity trên máy tính 64-bit

Chúng tôi phát triển trên máy 32 bit và do đó không biết vấn đề này cho đến khi được khách hàng thông báo. Tại sao sự mâu thuẫn đó xảy ra? Làm thế nào để ngăn chặn điều này xảy ra?

+2

Sản lượng '+ inf' trên cả máy 32 và 64 bit tại đây. – Joey

Trả lời

21

Các tập lệnh x86 có các vấn đề nhất quán về điểm nổi khó tính do cách FPU hoạt động. Tính toán nội bộ được thực hiện với các bit quan trọng hơn có thể được lưu trữ trong một đôi, gây cắt ngắn khi số lượng được flushed từ FPU stack vào bộ nhớ.

Điều đó đã được sửa trong trình biên dịch JIT x64, nó sử dụng lệnh SSE, thanh ghi SSE có cùng kích thước gấp đôi.

Điều này sẽ giúp bạn tính toán các ranh giới của độ chính xác và phạm vi của dấu phẩy động. Bạn không bao giờ muốn nhận được gần hơn cần nhiều hơn 15 chữ số đáng kể, bạn không bao giờ muốn nhận được gần 10E308 hoặc 10E-308. Bạn chắc chắn không bao giờ muốn vuông giá trị đại diện lớn nhất. Đây không bao giờ là một vấn đề thực sự, những con số đại diện cho số lượng vật lý không đến gần.

Sử dụng cơ hội này để tìm hiểu xem có vấn đề gì với tính toán của bạn. Điều rất quan trọng là bạn chạy cùng một hệ điều hành và phần cứng mà khách hàng của bạn đang sử dụng, thời gian cao để bạn có được các máy cần thiết để làm như vậy. Mã vận chuyển chỉ được thử nghiệm trên máy x86 không được kiểm tra.

Q & D sửa là Dự án + Thuộc tính, tab biên dịch, Nền tảng đích = x86.


Fwiw, kết quả xấu trên x86 do lỗi trong trình biên dịch JIT gây ra.Nó tạo mã này:

 double r = Math.Sqrt(v1 * v1); 
00000006 fld   dword ptr ds:[009D1578h] 
0000000c fsqrt    
0000000e fstp  qword ptr [ebp-8] 

Hướng dẫn fmul bị thiếu, bị xóa bởi trình tối ưu hóa mã trong chế độ phát hành. Không nghi ngờ gì được kích hoạt bởi nó nhìn thấy giá trị ở double.MaxValue. Đó là một lỗi, bạn có thể báo cáo nó tại connect.microsoft.com. Khá chắc chắn họ sẽ không sửa chữa nó mặc dù.

+0

"byte bạn" - chơi chữ có chủ ý, hoặc đánh máy? ;) –

+7

@Jonners: khá có chủ ý. –

1

Vấn đề là Math.Sqrt mong đợi một đối số gấp đôi. v1 * v1 không thể được lưu trữ dưới dạng double và overflows dẫn đến hành vi không xác định .

+1

Không có hành vi không xác định với IEEE 754. Một 'double' tràn ngập được định nghĩa là vô cùng tích cực và các quy tắc về cách thực hiện các hoạt động tiếp theo với điều này khá rõ ràng. – Joey

+0

@Johannes, cảm ơn vì đã giải thích rõ điều này. –

1

double.MaxValue * double.MaxValue là lỗi tràn.

Bạn nên tránh việc tính toán tràn thay vì dựa vào hành vi 32-bit mà bạn đã báo cáo (như nhận xét khi có vẻ không giống nhau).

[Có phải 32bit và 64bit xây dựng cấu hình và thiết lập tương tự?]

+0

Chính xác như cũ. –

2

tôi đã cố gắng này trong x86 và x64 trong gỡ lỗi và phát hành chế độ:

x86 debug: Double.MaxValue 
x64 debug: Infinity 
x86 release: Infinity 
x64 release: Infinity 

Vì vậy, có vẻ như nó chỉ trong chế độ gỡ lỗi mà bạn nhận được kết quả đó.

Không chắc lý do tại sao có sự khác biệt, tuy nhiên, mã x86 trong chế độ gỡ lỗi:

  double r = Math.Sqrt(v1 * v1); 
00025bda fld   qword ptr [ebp-44h] 
00025bdd fmul  st,st(0) 
00025bdf fsqrt    
00025be1 fstp  qword ptr [ebp-5Ch] 
00025be4 fld   qword ptr [ebp-5Ch] 
00025be7 fstp  qword ptr [ebp-4Ch] 

cũng giống như mã trong chế độ phát hành:

  double r = Math.Sqrt(v1 * v1); 
00000027 fld   qword ptr [ebp-8] 
0000002a fmul  st,st(0) 
0000002c fsqrt    
0000002e fstp  qword ptr [ebp-18h] 
00000031 fld   qword ptr [ebp-18h] 
00000034 fstp  qword ptr [ebp-10h] 
+0

Bạn không xem mã JIT được tối ưu hóa. Công cụ + Tùy chọn, Gỡ lỗi, bỏ chọn "Suppress JIT optimization". Bạn sẽ thấy rằng JITter không bận tâm khi tạo lệnh fmul. Đó là một lỗi. –

+0

@nobugz: Tôi đã thử điều đó, nhưng mã vẫn giống nhau. – Guffa

3

Đây là một bản sao đêm của

Why does this floating-point calculation give different results on different machines?

câu trả lời của tôi cho câu hỏi đó cũng trả lời thế này. Tóm lại: phần cứng khác nhau được phép cung cấp kết quả chính xác hơn hoặc ít hơn tùy thuộc vào chi tiết của phần cứng.

Làm cách nào để ngăn điều đó xảy ra? Vì vấn đề là trên chip, bạn có hai lựa chọn. (1) Không thực hiện bất kỳ phép tính nào trong các số dấu phẩy động. Làm tất cả toán học của bạn trong các số nguyên. Toán số nguyên là 100% nhất quán từ chip đến chip. Hoặc (2) yêu cầu tất cả khách hàng của bạn phải sử dụng cùng một phần cứng khi bạn phát triển.

Lưu ý rằng nếu bạn chọn (2) thì bạn vẫn có thể gặp sự cố; các chi tiết nhỏ như một chương trình đã được biên dịch gỡ lỗi hay bán lẻ có thể thay đổi cho dù các tính toán dấu chấm động được thực hiện chính xác hơn hay không. Điều này có thể gây ra các kết quả không nhất quán giữa các bản dựng gỡ lỗi và bán lẻ, điều này cũng bất ngờ và khó hiểu. Nếu yêu cầu nhất quán của bạn là quan trọng hơn yêu cầu của bạn về tốc độ thì bạn sẽ phải thực hiện thư viện dấu chấm động của riêng bạn mà thực hiện tất cả các phép tính của nó trong các số nguyên.

+0

Thật không may, chúng tôi cần chương trình chạy như 64-bit tinh khiết trên Windows 64 bit. Nó là một ứng dụng kỹ thuật mà làm tất cả các phép toán trong các số nguyên là không thực tế. –

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