为什么三次添加1.0 / 3.0的工作原理与数学预期相同?

问题描述 投票:1回答:2

我知道在大多数情况下,实数不能用二进制精确表示(即使有所谓的双精度)。例如,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))都小于实际值在二进制),我仍然没有弄清楚发生了什么。

如果您让我知道这个结果的可能原因,我将不胜感激。

c double
2个回答
1
投票

这是一个很好的四舍五入问题。如果我没记错的话,算术协处理器使用80位:64位精度位和15位指数(ref.)。这意味着内部操作使用的位数比显示的多。最后,协处理器实际上舍入其内部表示(更准确),以提供仅64位的值。并且当第一位下降是1而不是0时,结果向上舍入为1。

但我必须承认我只是在猜这里......

但是如果你试图手动执行操作,如果立即出现,则添加将所有精度位设置为1(将5555 ... 5和555 ... 5加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的最低有效位为零,因此这是加法的舍入结果。

© www.soinside.com 2019 - 2024. All rights reserved.