在gcc 4.7.3
上,我的fegetround()
函数返回FE_TONEAREST
。根据c++参考,这意味着从零开始四舍五入。从本质上讲,这意味着保存乘法后调整尾数精度时移出的最后一位(因为它的长度是应该的两倍)。然后,将保存的比特添加到最终的尾数结果中。
例如,浮点乘法给出以下结果:
0x38b7aad5 * 0x38b7aad5 = 0x3203c5af
相乘后的尾数是
1011 0111 1010 1010 1101 0101
x 1011 0111 1010 1010 1101 0101
-------------------------------
1[000 0011 1100 0101 1010 1110] [1]000 0101 1001 0101 0011 1001
[23'b]
集合保存有效数字,而[1'b]
集合保存最后一个移出的位。注意,结果的尾数是
[000 0011 1100 0101 1010 1111]
最后一位切换到1
,因为由于舍入模式,[1'b1]
集合被添加到拼接尾数([23'b]
集合)。
这是一个困扰我的例子,因为在我看来,硬件没有正确舍入。
0x20922800 * 0x20922800 = 0x1a6e34c (check this on your machine)
1010 0110 1110 0011 0100 1101
x 1010 0110 1110 0011 0100 1101
-------------------------------
01[01 0011 0111 0001 1010 0110 0][1]00 0000 0000 0000 0000 0000
Final Mantissas:
Their Result: 01 0011 0111 0001 1010 0110 0
Correct Result(?): 01 0011 0111 0001 1010 0110 1
我一整天都在处理二进制,所以我可能错过了一些简单的东西。在给定的舍入模式下,哪个答案是正确的?
当四舍五入到最近值时,IEEE指定将四舍五舍五入连接到偶数。0
是偶数,1
是奇数,所以Intel是正确的。
第一个四舍五入到最近的缺少一个细节。它是四舍五入到最近的(偶数)。
IEEE 754标准(第4.3.1节)引用:
roundTiesToEven,最接近无限精确结果的浮点数应为交付;如果包围一个不可无限表示的最近的两个浮点数精确的结果同样接近,具有偶数最低有效数字的结果应交付
在第一个示例中,您计算(8.75794e-5)的平方,该平方(如果表示为32位浮点)具有以下十六进制模式:0x38b7aad5
。
(8.75794e-5)的所有24个有效位均为:
0xb7aad5 = 1.0110111_10101010_11010101
现在平方后,你得到:
1.0000011_11000101_10101110_10000101_10010101_00111001
值得注意的是,在99%的情况下,您的计算将在FPU(可能是x87)上执行,该FPU以80位浮点格式运行。
英特尔®;64与IA-32体系结构软件开发人员手册
(用X87 FPU编程):
浮点、整数或压缩BCD整数时值从内存加载到任何x87 FPU数据寄存器中,值为自动转换为双扩展精度浮点格式(如果尚未采用该格式)。
现在您想将结果存储在32位浮点:中
1.[0000011_11000101_10101110]10000101_10010101_00111001
这就是舍入模式的重要性所在。IEEE 754定义了其中的4个,但在这里讨论这一个时,让我们关注默认的一个(四舍五入到最接近(偶数))。
现在您的FPU已经计算出了结果(整体-我们有80位格式的64个有效位),它必须执行四舍五入以将数字拟合在32位(24个有效位位)内。需要显式存储的所有23个比特都放在上面的括号内。
现在,四舍五入与无关,甚至在这种特殊情况下单词,因为括号右侧的位是而不是中间位置:
1.[0000011_11000101_10101111]
and
1.[0000011_11000101_10101110]
但他们更接近
1.[0000011_11000101_10101111]
这就是为什么结果的有效位是0x3203C5AF
。
现在平方2.4759832E-19 0x20922800
的结果有问题。
2.4759832E-19的24个有效位为:
0x922800 = 1.0010010_00101000_0000_0000
和平方:
1.[0100110_11100011_01001100]10000000_00000000_0000000
这就是甚至部分真正重要的地方。现在,您的价值正好介于:
1.[0100110_11100011_01001101]
and
1.[0100110_11100011_01001100]
以上2个值表示您的值。现在,您甚至需要从中选择一个(后者因为lsb=0)。
现在您知道为什么结果的24位是0xA6E34C
(最近的偶数)而不是0xA6E34D
(最近但奇数)