簡體   English   中英

Binary64浮點加法舍入模式錯誤和行為差異32/64位

[英]Binary64 floating point addition rounding mode error and behaviors difference 32/64 bits

當我嘗試在Intel核心I7 / I5上添加以下兩個浮點數時,我注意到一個舍入錯誤:

2.500244140625E + 00 + 4503599627370496.00 <=> 0x1.4008p + 1 + 0x1.0p + 52

加法faddlfaddl匯編指令使用兩個double精度常量進行(當我使用32位編譯器進行編譯時)。

我得到的結果是:

4.50359962737049 8 E + 15 = 0x1.000000000000 2 p + 52

代替 :

4.50359962737049 9 E + 15 = 0x1.000000000000 3 p + 52

(如我所料,並已通過http://weitz.de/ieee/確認)。

示范:

0x1.0p + 52 = 0x10000000000000.00p + 0

0x1.4008p + 1 = 0x2.801p + 0

0x10000000000000.00p + 0 + 0x2.801p + 0 = 0x10000000000002.801p + 0 (完全)

0x10000000000002.801p + 0 = 0x1.0000000000002 8 01p + 52 (完全)

0x10000000000002.801p + 0 = 0x1.000000000000 3 p + 52 (四舍五入后)

我仔細檢查並在調試模式下驗證我的FPU是否處於“四舍五入到最近的模式”。

更為奇怪的是,當我使用64位編譯器編譯代碼,然后使用addsd指令時, 沒有舍入錯誤

有誰能給我關於相同FPU但使用不同指令集的“雙”加法精度差異的參考或解釋?

FPU寄存器為80位寬,每當將fld及其變體裝入單精度或雙精度數字時,默認情況下1會將其轉換為雙精度擴展精度
因此, fadd通常適用於80位數字。

SSE寄存器與格式無關,SSE擴展不支持雙精度擴展精度。
例如, addpd使用雙精度數字。


默認的四舍五入模式是四舍五入到最接近(偶數) ,這意味着通常的四舍五入到最接近,但在出現平局的情況下朝着偶數結束(例如4.5 => 4)。

要實現IEEE 754要求以無限精度數字執行算術運算,硬件需要兩個保護位和一個粘性位2


我將寫一個雙精度數字為

<sign> <unbiased exponent in decimal> <implicit integer part> <52-bit mantissa> | <guard bits> <sticky bit>

兩個數字

2.500244140625
4503599627370496

+  1 1 0100000000 0010000000 0000000000 0000000000 0000000000 00
+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 00

第一個轉移了

+ 52 0 0000000000 0000000000 0000000000 0000000000 0000000000 10 |10 1   
+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 00 |00 0

總和完成

+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 10 |10 1

四舍五入到最接近的(偶數)

+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 11

因為0 |10 10 |00 0更接近1 |00 0 0 |00 0

雙重擴展

這兩個數字是

+  1 1 0100000000 0010000000 0000000000 0000000000 0000000000 0000000000 000
+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 000

首先是轉移

+ 52 0 0000000000 0000000000 0000000000 0000000000 0000000000 1010000000 000 | 10 0
+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 000 | 00 0

總和完成

+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 1010000000 000 | 10 0

四舍五入到最接近的(偶數):

+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 1010000000 000

0 | 10 0 0 | 10 0被平局到最接近的偶數。

然后將此數字從雙精度擴展精度轉換為雙精度精度時(由於fstp QWORD [] ),使用雙精度擴展尾數的第52、53和54位作為保護和粘性位,重復進行舍入

+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 1010000000 000

+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 10|100

+ 52 1 0000000000 0000000000 0000000000 0000000000 0000000000 10

因為0|100再次被平分到最接近的偶數。


1請參閱《英特爾手冊-第1卷》第8.5.1.2章。
2保護位是在數字之一移位以使指數匹配后保留的超精度位。 粘性位是比最小防護位低的位的或。 請參閱本頁的“四舍五入”部分和Goldberg的格式方法。

感謝我的問題收到的所有評論,我了解了發生的情況並能夠解決問題。

我將在這里總結一下。

首先,確認不正確的舍入。 @MarkDickinson所述 ,這可能是由於“雙舍入”引起的,但我不知道是否可以確認。 確實,這也可能是由於其他現象,例如Pascal Cuoq給出的出版物中描述的現象。

ia32 FPU在四舍五入某些數字時似乎並不完全符合IEEE754標准。

默認情況下,GCC(32位版本)生成使用FPU來計算Binary64數字上的加法的代碼。

但是,在我的計算機(Intel Core i7)上,SSE單元也能夠進行這些計算。 默認情況下,GCC(64位版本)使用此單位。

在GCC32命令行上使用以下兩個選項可以解決我的問題。

-msse2 -mfpmath = sse。

(感謝你EOF

首先,您要查看以10為基數的數字。 您想討論浮點和舍入,因此需要以2為基礎進行討論。

第二個單精度和雙精度尾數的尾數不同,因此對於相同的數字,顯然您舍入的位數以十進制1.2345678進行舍入,我們可以將其舍入為1.23或將其舍入為1.2346,具體取決於我們允許多少個數字向上舍入一位向下舍入。匯總規則。

由於您此時的基礎是10,因此您還會混入編譯時轉換,運行時操作和運行時轉換

我拿

float x=1.234567;
x=x*2.34;
printf("%f\n",x);

有編譯時間轉換,首先將ascii加倍,然后將double加倍,將float浮動,以完全准確地理解該語言(將F放在常量的末尾)。 然后將運行時間相乘,然后將運行時轉換為ascii,則運行時C庫可能與編譯時不同,它們是否接受相同的舍入設置,等等。很容易找到只需聲明x =的數字1.234 ...某些東西,然后下一行代碼是printf,而printf不是您提供的內容,除了運行時浮點數是int之外,沒有浮點數。

因此,在提出這個問題之前,我們需要查看數字的二進制形式,這個問題的答案應該在沒有其他幫助的情況下幾乎自動消失。但是,如果您仍然需要幫助,則可以發布它,我們可以進行研究。 進行基於十進制的討論會增加編譯器和庫問題,並且如果存在問題,則更難找出問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM