为何三次累加1.0/3.0能得到精确1.0?二进制浮点运算疑问
为什么三个1/3的双精度累加会得到精确的1.0?
这其实是浮点数舍入规则和双精度表示特性共同作用的结果,咱们一步步拆解来看:
1. 先纠正一个误解:双精度对1/3的近似值其实略大于真实值
你可能误以为1.0/3.0的双精度表示比真实的1/3小,但实际刚好相反:
- 真实的1/3是无限循环的二进制小数
0.(01)₂,无法用有限位的双精度精确表示。 - 双精度(64位)会把它舍入到最近的可表示值,也就是
0x3fd5555555555555,对应的十进制是0.33333333333333331483——这个值比真实的1/3大了约1.48e-17。
2. 前两次累加的结果依然略大于真实的2/3
两个0x3fd5555555555555相加得到0x3fe5555555555555,对应十进制0.66666666666666662966,比真实的2/3大了约2.96e-17,这部分数值符合你的观察,但要注意它是偏大的,不是偏小。
3. 第三次累加触发了舍入到1.0的规则
第三次加上同一个1.0/3.0的近似值后,总和是:0.66666666666666662966 + 0.33333333333333331483 = 0.99999999999999994449
现在看双精度中这个值附近的两个可表示数:
- 1.0本身:
0x3ff0000000000000,对应精确的1.0 - 比1.0小的下一个可表示值:
0x3fefffffffffffff,对应0.99999999999999988898
计算这个总和到两个值的距离:
- 到1.0的距离:
1.0 - 0.99999999999999994449 = 5.55e-17 - 到下一个更小值的距离:
0.99999999999999994449 - 0.99999999999999988898 = 5.55e-17
刚好距离相等!这时候双精度默认的舍入到最近偶数规则就生效了:当两个候选值距离相等时,会选择最低有效位为偶数的那个。1.0的尾数全是0(偶数),而0x3fefffffffffffff的尾数全是1(奇数),所以最终结果被舍入到了精确的1.0。
验证:这不是编译器优化的结果
你可能会怀疑是不是编译器直接把三个1.0/3.0优化成了1.0,但即使阻止编译器优化(比如用volatile修饰变量),结果依然是0x3ff0000000000000:
#include<stdio.h> int main(){ volatile double a = 1.0/3.0; double result = a; result += a; result += a; printf("%016llx\n", (unsigned long long)result); }
这说明这个结果是浮点数加法舍入的自然结果,而非编译器的“特殊处理”。
内容的提问来源于stack exchange,提问作者Katie Imach
相关产品推荐
相关产品推荐

