2015-10-15 94 views
23

如果我的機器上運行該語句更改Math.Exp或雙實施.NET 4.5.2

Math.Exp(113.62826122038274).ToString("R") 

安裝.NET 4.5.1,然後我得到的答案

2.2290860617259248E+49 

但是,如果我的機器上運行相同的命令與安裝了.NET Framework 4.5.2,然後我得到的答案

2.2290860617259246E+49 

(即最終d igit變化)

我意識到,這是純粹的數字大致而言微不足道,但沒有人知道的已經在.NET 4.5.2可以解釋的變化作出任何改變?

(我沒有一個結果,更喜歡其他的,我只是有興趣瞭解爲什麼它已經改變)

如果我輸出

The input in roundtrip format 
The input converted to a long via BitConverter.DoubleToInt64Bits 
Math.Exp in roundtrip format 
Math.Exp converted to a long via BitConverter.DoubleToInt64Bits 

然後在4.5.1,我得到

113.62826122038274 
4637696294982039780 
2.2290860617259248E+49 
5345351685623826106 

和4.5.2,我得到:

113.62826122038274 
4637696294982039780 
2.2290860617259246E+49 
5345351685623826105 

因此,對於完全相同的輸入,我得到一個不同的輸出

更多細節(這可以從比特所以沒有往返格式參與可以看出):

編譯一次使用VS2015

這兩款機器我正在運行的二進制文件是64位

一個已安裝.NET 4.5.1,其他4.5.2

只是爲了清晰:字符串轉換是不相關的。我得到的結果REGA變化無論是否涉及字符串轉換。我提到純粹是爲了展示變化。

+0

也安裝了net46嗎? – Dbl

+0

@AndreasMüller - 不,只是4.5.2,雖然4.6顯示了與4.5.2相同的答案 - 更改似乎是由4.5.2引入的,而不是4.6 –

+0

您是否正在編譯每臺機器上的代碼?如果是這樣,其中一個使用Roslyn,另一個使用「舊」編譯器?這可以解釋事情 - 但爲了減少事情,我建議刪除'Math.Exp'調用,並打印出'113.62826122038274.ToString(「R」)'。如果我的懷疑是正確的,你也會看到不同的結果。 –

回答

7

.NET使用數學LIB功能從CRT做這些計算。由.NET所使用的CRT經常與各版本更新,所以你可以期待的結果,.NET版本之間發生變化,但是,他們永遠是承諾+/1ulp內。

+0

什麼是CRT? – flq

+0

@flq - C運行時。它的微軟C編譯器附帶的C標準庫。這個SO帖子有很多很好的信息:http:// stackoverflow。com/questions/2766233/what-is-c-runtime-library – antiduh

1

我絆倒了同樣的問題,但我只安裝的.Net 4.6後得到它。

.NET 4.6安裝升級了c:\ windows \ system32 \ msvcr120_clr0400.dll和c:\ windows \ syswow64 \ msvc120_clr0400.dll。

安裝這些DLL文件有屬性 - >細節之前:「文件版本12.0.51689.34249」和「產品名稱微軟的Visual Studio 12 CTP」

安裝.NET 4.6後,這些原本「文件版本12.0.52512.0 「和‘產品名稱微軟的Visual Studio 2013’

我調整我的測試,包括你的榜樣,我看到相同的號碼,你前/後該DLL的版本之間翻轉。

(.NET的版本4,4.5運行時,我們的測試套件,並沒有表現出任何改變的結果,4.5.1,4.5.2或直到這些DLL進行了更新。)

11

嘆息,浮動的奧祕點數學繼續爲程序員樹立了永恆的烙印。它與框架版本沒有任何關係。相關設置是項目>屬性>生成選項卡。

目標平臺= 86:2.2290860617259248E+49
目標平臺= AnyCPU或x64:2.2290860617259246E+49

如果您運行32位操作系統上的程序,那麼你總是得到的第一個結果。請注意,往返的格式是過分指定的,它包含的數字比雙重罐頭商店多。這是15.計數它們,你得到16.這確保了二進制表示的雙倍數,1s和0s是相同的。這兩個值之間的差異是尾數中的最低位。

LSB不一樣的原因是因爲x86抖動受the FPU生成代碼的影響。這具有使用更多位的精度比雙可存儲的非常不利的性質。 80位而不是64位。理論上可以生成更準確的計算結果。它的確如此,但是rarely in a reproducible way。對代碼的小改動會對計算結果產生較大的改變。只需使用附加的調試器運行代碼就可以改變結果,因爲禁用了優化器。

Intel用SSE2指令集修正了這個錯誤,完全取代了FPU的浮點運算指令。它不使用額外的精度,double總是有64位。由於計算結果現在不再依賴於中間存儲,所以非常令人滿意,現在它更加一致。但不太準確。

x86抖動使用FPU指令是歷史事故。 2002年發佈的支持SSE2的處理器數量不足。由於它改變了程序的可觀察行爲,因此無法再修復這一事故。對於x64抖動來說這不是問題,64位處理器也保證也支持SSE2。

32位進程使用使用FPU代碼的exp()函數。一個64位的進程使用使用SSE代碼的exp()函數。結果可能會因一個LSB​​而不同。但仍精確到15位有效數字,它是2.229086061725925E + 49。所有你可以期望的數學與

+0

我不確信這解釋了「Math.Exp在安裝.NET 4.6.1並應用KB3098785後返回不同的結果」https://connect.microsoft .com/VisualStudio/feedback/details/2486915/math-exp-returns-different-results –

+0

我告訴過你該怎麼做。請親自嘗試,更改平臺目標並觀察x86生成0.71612515940795685和x64生成0.71612515940795696。與框架版本無關,與FPU與SSE2相關的一切。 –

+0

在我的情況下任何CPU給了2.2290860617259248E + 49,因爲首選32位被設置。但一般來說,你的答案也是一樣。 – cassandrad

1

爲了展示如何針對不同.NET版本的項目影響字符串的雙重轉換,我構建了4個項目,針對不同版本,全部運行在.NET 4.6的同一開發機器上。

下面的代碼

double foo = Convert.ToDouble("33.94140881672595"); 

而這裏的輸出

33.941408816725954(.NET 4)

33.941408816725946(.NET 4.5)

33.941408816725946(.NET 4.5.2)

33.941408816725946(.NET 4。6)

因此,.NET 4後的轉換方法肯定有變化