繁体   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