2017-08-17 121 views
1

acos(double)給出了x64和X32的Visual Studio不同的結果。ACOS(雙)給出了x64和X32的Visual Studio不同的結果

printf("%.30g\n", double(acosl(0.49990774364240564))); 
printf("%.30g\n", acos(0.49990774364240564)); 
在x64

1.0473040763868076
上X32:1.0473040763868078

上linux4.4 x32或x64的與上證所啓用:1.0473040763868078

有沒有一種方法,使VSx64 acos()給我1.0473040763868078的結果?

+0

轉載我在VS2015:C:\項目\ ConsoleApplication1 \發佈> ConsoleApplication1.exe中 1.04730407638680778070749965991 1.04730407638680778070749965991 C:\項目\ ConsoleApplication1 \ 64 \發佈> ConsoleApplication1.exe中 1.04730407638680755866289473488 1.04730407638680755866289473488 – Artimosha

+0

什麼樣的回答是你在找什麼? C++標準允許這樣做(只有+ - * /和sqrt才能生成正確舍入的結果到尾數的最後一位)。那麼你是否要求提供不同庫實現的asm級別細節?確定性FP計算是一個非常難的問題,AFAIK通常必須避免更復雜的庫函數。即便如此,不同的編譯器和不同的體系結構使得C++非常困難。 –

+0

@Artimosha:爲什麼這對你來說是個問題? IEEE754不保證超越函數的最準確結果。它只能保證四個基本操作和sqrt。 – geza

回答

2

TL:DR:這是正常的,你不能合理地改變它。


32位文庫可以使用其臨時對象的x87寄存器80位FP值,避免每次操作後四捨五入到64位double。 (除非有一個完整而獨立的庫,編譯自己的代碼使用SSE不會改變什麼的圖書館,甚至將數據傳遞到庫調用約定內,但是由於32位內存通過doublefloat堆棧上,一個庫可以自由地加載SSE2或者x87,但是你不會獲得在xmm寄存器中傳遞FP值的性能優勢,除非非SSE代碼不可能使用這個庫)。

它是也可能因爲他們使用不同的操作順序而產生不同的臨時對象。這不太合理,除非他們分別手寫在asm中。如果它們是用相同的C源代碼構建的(沒有「不安全的」FP優化),那麼由於FP數學的這種非關聯行爲,編譯器不允許重新排序。


的glibc的libm中(在Linux上使用)通常有利於精確的過速,因此它給你正確的舍入結果出尾數爲32位和64位的最後一位。 IEEE FP標準只要求將基本操作(+ - */FMA和FP餘數)「正確舍入」到尾數的最後一位。 (即舍入誤差最多爲0.5 ulp)。 (確切的結果,根據calc,是1.047304076386807714...。請記住,double(與普通編譯器86)是IEEE754 binary64,所以內部的尾數和指數都在BASE2。如果打印足夠的額外十進制數字,不過,你可以告訴這...7714應該四捨五入到...78,但真的是你應該打印更多位數的情況下,他們沒有零不止於此。我只是假設它的...78000。)

所以微軟的64位庫的實現產生1.0473040763868076和有幾乎沒有什麼可以做的,除了不使用它。 (例如,找到自己的acos()實現並使用它。)但FP確定性是hard,即使您將自己限制爲僅使用SSE的x86。見Does any floating point-intensive code produce bit-exact results in any x86-based architecture?。如果將自己限制爲單個編譯器,那麼可以避免使用像acos()這樣的複雜庫函數。

如果使用x87並更改x87精度設置影響它,您可能可以獲得32位庫版本以生成與64位版本相同的值。但相反方式是不可能的:SSE2對於64位double和32位float有單獨的指令,並且總是在每條指令之後循環,所以不能更改任何會提高精度結果的設置。 (你可以改變上證所舍入模式,這將改變結果,但不是一個好辦法!)

參見:

2

您可能已達到精度限制。 Double precision is approximately 16 digits。之後,不保證數字是有效的。如果是這樣,除了將double類型更改爲其他類型之外,您無法更改此行爲,從而支持更高的精度。

E.g.如果您的機器和編譯器支持extended 80 bit double或128位Quadruple(例如,也取決於機器,請參閱here)。

+2

真正的問題是計算過程中臨時對象的精度,如果它使用x87指令,則對於32位庫函數可能是80位。 64位版本在每一步中只使用64位「double」,因爲AMD64包含SSE2並且默認情況下使用。或者乾脆以不同的順序執行相同的操作可能會產生不同的結果(通過對中間數進行四捨五入),即使兩個版本都使用SSE2。但是,只有在庫啓用了不安全的FP優化的情況下才會出現這種情況。 (FP數學不關聯,編譯器/標準知道這一點)。 –

+0

@PeterCordes我不知道64位機器只會使用64位雙。好點,謝謝。 – user1810087

1

我不同意,有沒有什麼可以做的。

例如,您可以嘗試更改浮點型號編譯器選項。

這裏是我的結果與不同的浮點模型(注意/fp:precise是默認值):

/fp:precise 1.04730407638680755866289473488 
/fp:strict 1.04730407638680755866289473488 
/fp:fast  1.04730407638680778070749965991 

因此,看來你正在尋找/fp:fast。儘管如此,是否能給出最準確的結果還有待觀察。

+0

我的錯誤;我不知道VC++對不同的FP設置有不同的數學庫實現。如果我用'calc'進行的非常簡短的測試是正確的,那麼具有諷刺意味的是'/ fp:fast'結果正確舍入,而其他結果不是*這個特定的輸入值。 –

相關問題