[英]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
加法faddl
由faddl
匯編指令使用兩個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 1
比0 |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.