2010-03-17 305 views
11

考慮下面的代碼不一致數學運算的結果:C# - 在32位和64位

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

R = double.MaxValue在32位機器 R = 64位機器上無限

我們在32位機器上開發,因此不知道這個問題,直到客戶通知。爲什麼會出現這種不一致?如何防止這種情況發生?

+2

這裏在32位和64位機器上都會產生'+ inf'。 – Joey 2010-03-17 10:12:27

回答

21

x86指令集有由於FPU的工作方式而引起的棘手的浮點一致性問題。內部計算的執行位數高於可存儲在double中的位數,當數字從FPU堆棧刷新到內存時導致截斷。

在x64 JIT編譯器中得到修復,它使用SSE指令,SSE寄存器與double有相同的大小。

當您的計算測試浮點精度和範圍的邊界時,這將以字節爲單位。你永遠不想接近需要超過15位有效數字,你永遠不想接近10E308或10E-308。你當然不希望劃分最大的可表示價值。這絕不是一個真正的問題,代表物理量的數字不會接近。

利用這個機會找出你的計算出了什麼問題。運行客戶正在使用的相同操作系統和硬件非常重要,您需要很長時間才能獲得所需的機器。僅在x86機器上測試的運輸代碼未經測試。

Q & D修復程序是Project + Properties,Compile選項卡,Platform Target = x86。


Fwiw,x86上的錯誤結果是由JIT編譯器中的錯誤引起的。它生成此代碼:

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

fmul指令丟失,由代碼優化器在發佈模式中刪除。毫無疑問,它會觸發它在double.MaxValue處看到的值。這是一個錯誤,你可以在connect.microsoft.com上報告它。很確定他們不會修復它。

+0

「字節你」 - 故意雙關或錯字? ;) – 2010-03-17 10:50:48

+7

@Jonners:非常有意。 – 2010-03-17 10:57:03

1

問題是,Math.Sqrt期望雙重作爲參數。 v1 * v1不能存儲爲雙精度且溢出 導致未定義的行爲

+1

IEEE 754沒有未定義的行爲。溢出的'double'被定義爲正無窮,並且規則如何進行進一步的操作是非常清楚的。 – Joey 2010-03-17 10:13:41

+0

@Johannes,謝謝你的澄清。 – 2010-03-17 10:14:39

1

double.MaxValue * double.MaxValue是溢出。

您應該避免計算溢出,而不是依賴於您報告的32位行爲(正如評論似乎不喜歡)。

[是32位和64位建立相同的配置和設置?]

+0

完全一樣。 – 2010-03-18 07:23:19

2

我在調試和發佈模式,嘗試這種在x86和x64:

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

因此,它似乎只是在調試模式,你會得到這個結果。

不知道爲什麼是有區別的,雖然在調試模式下的x86代碼:

  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] 

是一樣的在釋放模式代碼:

  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

你沒有看JIT優化的代碼。工具+選項,調試,取消選中「取消JIT優化」。你會看到JITter不會生成fmul指令。這是一個錯誤。 – 2010-03-17 10:34:17

+0

@nobugz:我試過了,但代碼仍然是一樣的。 – Guffa 2010-03-17 10:45:37

3

這是

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

我回答這個問題,還回答了這一個近了重複。總之:根據硬件的細節,允許不同的硬件提供更多或更少的準確結果。

如何防止它發生?由於問題出在芯片上,你有兩種選擇。 (1)不要用浮點數做任何數學運算。用整數完成所有數學。整數數學從芯片到芯片是100%一致的。或者(2)要求所有客戶在開發時使用相同的硬件。

請注意,如果您選擇(2),那麼你可能仍然有問題;例如程序是否被編譯調試或零售這些小細節可以改變浮點計算是否以更高的精度完成。這可能會導致調試和零售版本之間的結果不一致,這也是意外和令人困惑的。如果你的一致性要求比你的速度要求更重要,那麼你將不得不實現你自己的浮點庫來完成所有的整數計算。

+0

不幸的是,我們需要該程序在64位Windows上以純64位運行。這是一個工程應用,在整數中進行所有的數學計算是不切實際的。 – 2010-03-18 07:35:03