舍入不像我期望的浮点减法那样工作.
IEEE754-2008,4.3.1说"roundTiesToEven ......如果包含无法代表的无限精确结果的两个最近的浮点数同样接近,那么具有最低有效数字的那个将被传递"
英特尔文档称这是默认模式并更明确地定义它:
https://software.intel.com/en-us/node/503710
我将首先介绍浮点加法 - 这正如我所期望的那样 - 来验证我的假设.然后,我会稍微修改我的测试用例,以显示我的理解中断的地方.
我们取两个单浮点数,这里用'binary32'格式表示:
S Exponent Significand
0 10000010 00000000000000000000001 (0x41000001)
130
0 01111111 00000000000000000000100 (0x3F800004)
127
Run Code Online (Sandbox Code Playgroud)
要添加它们,我将数字的有效位数用较小的指数(第二个操作数)移动到右边3个位置(我还在这里添加了隐含的前导1):
1.00000000000000000000001
+ 0.00100000000000000000000 100
----------------------------
1.00100000000000000000001
Run Code Online (Sandbox Code Playgroud)
因为移出的值在两者之间的中间位置(100),所以它应该舍入为偶数值的总和:
1.00100000000000000000010
Run Code Online (Sandbox Code Playgroud)
在二进制文件中,完整值为:
0 10000010 00100000000000000000010 (0x41100002)
Run Code Online (Sandbox Code Playgroud)
我可以验证这个:
#include <stdint.h>
#include <stdio.h>
union uval {
float fval;
int32_t ival;
};
int main()
{
union uval a, b, c;
a.ival = 0x41000001;
b.ival = 0x3F800004;
c.fval = a.fval + b.fval;
printf("%08x\n", c.ival);
}
Run Code Online (Sandbox Code Playgroud)
打印:
./a.out
41100002
Run Code Online (Sandbox Code Playgroud)
如果我在第一个操作数中添加一个:
a.ival = 0x41000002;
Run Code Online (Sandbox Code Playgroud)
我得到了同样的东西.它已经向下舍入到均匀的结果:
./a.out
41100002
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.但是,如果我使用原始值并通过将符号位设置为1将第二个参数修改为负值:
a.ival = 0x41000001;
b.ival = 0xBF800004;
Run Code Online (Sandbox Code Playgroud)
我明白了:
./a.out
40E00001
Run Code Online (Sandbox Code Playgroud)
这是:
0 10000001 11000000000000000000001
129
Run Code Online (Sandbox Code Playgroud)
在第二个参数对齐后,结果应该仍然在两个值之间.在这种情况下,为什么它没有达到均值?
要理解最终结果,重要的是要记住舍入是IEEE 754算法的最后一步.它通过标准化完成,然后舍入.
查看两个计算的高有效结束,两个操作数在显式位中都为零.隐含的一位b右移三个位置以匹配指数:
1.000
0.001
Run Code Online (Sandbox Code Playgroud)
添加这些给出1.001,因此指数保持相同a,并且结果中有一个明确的一位.
减去它们得到0.111.归一化将此左移一位,以消除前导0,给出1.110.结果中存储了两个显式的一位.
现在看看低意义的结局.原始减法将在"中间"位置留下一位.由归一化引起的左移将其转换为最不重要的存储位,结果是精确的.