我知道在大多数情况下,实数不能用二进制精确表示(即使有所谓的双精度)。例如,1.0 / 3.0近似为0x3fd5555555555555,实际上代表0.33333333333333331483 ....如果我们执行(1.0 / 3.0)+(1.0 / 3.0),那么我们获得0x3fe5555555555555(所以0.66666666666666662965 ......),正如预期的那样计算机算术感。
然而,当我尝试通过编写以下代码来执行(1.0 / 3.0)+(1.0 / 3.0)+(1.0 / 3.0)时
#include<stdio.h>
int main(){
double result=1.0/3.0;
result+=1.0/3.0;
result+=1.0/3.0;
printf("%016llx\n",result);
}
并使用标准GNU C编译器进行编译,然后生成的程序返回0x3ff0000000000000(正好代表1)。这个结果让我感到困惑,因为我最初期望0x3fefffffffffffff(我没想到舍入错误会相互抵消,因为(1.0 / 3.0)和((1.0 / 3.0)+(1.0 / 3.0))都小于实际值在二进制文件中),我仍然没有弄清楚发生了什么。
如果您让我知道此结果的可能原因,我将不胜感激。
答案 0 :(得分:1)
这是一个很好的舍入问题。如果我没记错的话,算术协处理器使用80位:64位精度位和15位指数(ref.)。这意味着内部操作使用的位数比显示的多。最后,协处理器实际上舍入其内部表示(更准确),以提供仅64位的值。并且当第一位丢弃是1而不是0时,结果向上舍入为1。
但我必须承认我只是猜测......
但是如果您尝试手动执行 操作,如果立即执行,则添加将所有精度位设置为1(将5555 ... 5和555 ... 5添加为1) 加上第一个丢弃的位也是1 。所以手工一个普通人会向上舍入也给1,因此算术单位也能够进行正确的舍入也就不足为奇了。
答案 1 :(得分:1)
没有必要考虑80位表示 - Java中的结果是相同的,除了一些不相关的边缘情况外,其行为与IEEE 754 64位二进制算法的行为相同。
1.0/3.0
的确切值是0.333333333333333314829616256247390992939472198486328125
只要涉及的所有数字都在正常范围内,乘以或除以2的幂是精确的。它只改变指数,而不是有效数。特别是,将1.0/3.0
添加到自身是准确的,因此第一次添加的结果是0.66666666666666662965923251249478198587894439697265625
第二次添加确实涉及四舍五入。确切的总和是0.99999999999999988897769753748434595763683319091796875,其由可表示的数字括起来0.999999999999999944488848768742172978818416595458984375和1.0。确切的值是包围数字的一半。必须丢弃一个比特。 1.0的最低有效位为零,因此这是添加的舍入结果。