Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么三次添加1.0/3.0的效果与数学上预期的一样?_C_Double - Fatal编程技术网

C 为什么三次添加1.0/3.0的效果与数学上预期的一样?

C 为什么三次添加1.0/3.0的效果与数学上预期的一样?,c,double,C,Double,我知道在大多数情况下,实数不能用二进制精确表示(即使是所谓的双精度)。例如,1.0/3.0近似为0x3FD5555555,它实际上表示0.3333333 1483。。。。如果我们执行(1.0/3.0)+(1.0/3.0),那么我们将获得0x3FE555(因此0.66666666662965…),这与计算机算术的预期一样 但是,当我试图通过编写以下代码来执行(1.0/3.0)+(1.0/3.0)+(1.0/3.0)时 #include<stdio.h> int main(){

我知道在大多数情况下,实数不能用二进制精确表示(即使是所谓的双精度)。例如,1.0/3.0近似为0x3FD5555555,它实际上表示0.3333333 1483。。。。如果我们执行(1.0/3.0)+(1.0/3.0),那么我们将获得0x3FE555(因此0.66666666662965…),这与计算机算术的预期一样

但是,当我试图通过编写以下代码来执行(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);
}
#包括
int main(){
双结果=1.0/3.0;
结果+=1.0/3.0;
结果+=1.0/3.0;
printf(“%016llx\n”,结果);
}
然后使用标准GNU C编译器编译它,结果程序返回0x3ff0000000000000(正好代表1)。这个结果让我感到困惑,因为我最初期望0x3FEFFFFFFFFFFFFFFFFF(我不期望舍入误差相互抵消,因为(1.0/3.0)和((1.0/3.0)+(1.0/3.0))在用二进制表示时都小于实际值),我仍然没有弄清楚发生了什么

如果您能告诉我这个结果的可能原因,我将不胜感激。

这是一个很好的四舍五入问题。如果我没记错的话,算术协处理器使用80位:64位精度位和15位指数()。这意味着该操作在内部使用的位超过了您可以显示的位。最后,协处理器实际上对其内部表示进行了四舍五入(更精确)以给出一个仅64位的值。由于第一个下降的位是1而不是0,所以结果向上取整,得到1

但我必须承认我只是在猜测


但是,如果您尝试手动执行该操作,如果加法立即将所有精度位设置为1(将5555…5和555…5相加,移位1)加上第一个要删除的位,也是1。所以,用手,正常人会向上倒1,所以算术单元也能做正确的舍入,这并不奇怪。< /P> < P>不需要考虑80位表示-在java中结果是相同的,除了一些不相关的边缘情况之外,与IEEE 754 64位二进制算法的双精度运算行为相同

1.0/3.0
的精确值为0.33331482961625624739099939472198486328125

只要所有涉及的数字都在正常范围内,乘以或除以二的幂就是准确的。它只改变指数,不改变有效位。特别是,将
1.0/3.0
添加到自身是精确的,因此第一次添加的结果是0.6666666629659232512494947819858789443969265625


第二个加法确实涉及舍入。精确总和为0.999999999999988897769753748434595763683319091796875,由可表示数字0.9999999999994448884876874217297818416595458984375和1.0括起。精确值介于括号中的数字之间。必须删除一个位。1.0的最低有效位是零,因此这是加法的四舍五入结果。

“实数不能用二进制精确表示”这不是二进制系统独有的。十进制系统也不能显示一些没有无限精度的数字。Re“即使有所谓的双精度”,也没有所谓的双精度。单精度浮点的精度为24位,而双精度浮点的精度为53位浮点的两倍多一点。由于这是一个问答论坛,如果您发表评论,让他们添加一些有意义的内容来解决此处提出的问题或澄清问题,我将不胜感激,虽然我考虑删除第一个句子,如果它会导致不必要的混乱,但它并不像基2中的数学值那么多,因为它在基10中。您可以通过添加printf(“%.19f\n”,result)看到的内容;在变量赋值之后。你会看到0.33…33148,而不是0.33…33000。四舍五入仍然使其达到1.0。只需将其添加5次,使其累积到足够明显的程度。您的代码具有未定义的行为,因为格式字符串指定正在格式化的参数的类型为
long long unsigned
,并且该参数的类型实际上为
double
。当行为未定义时,对发生的情况的解释隐含地依赖于编译器。请使用指定类型为
double
的格式。非常感谢您的回答。你能让我确认一下我的理解是否正确吗?这是我目前的理解:当我们执行(1.0/3.0)+(1.0/3.0)时,算术协处理器中的结果尾数是0x555…5(十六进制16位,64位),第一个删除的第53位是0。因此,显示结果时,结果向下四舍五入。相反,当我们执行(1.0/3.0)+(1.0/3.0)+(1.0/3.0)时,尾数是0xfff…f,第一个下降的位是1,因此结果向上取整,得到的正好是1。“这是对的吗?”凯蒂马克:正如我已经说过的,我只是在猜测。但我认为这是正确的…在这种情况下,我会接受你的答案,尽管我必须研究一些文件来说服自己。非常感谢你!非常感谢你的回答。你能让我确认一下我的理解是否正确吗?这是我目前的理解:第二次加法的确切结果是二进制表示的0x3FEFFFFFFFFF8(十六进制的17位数字)。由于精确表示法在二进制表示法中的位数多于64位,因此必须执行舍入,精确结果正好在0x3FFFFFFFFFFFFF和0x3ff0000000000000之间的一半。因此,执行舍入操作,使最低有效位默认为0,即0x3ff0000000000000(dicimal表示为1.0)。对吗?谢谢。也许我应该咨询一下SO中现有的其他Q&a,但我还有一个问题:计算机如何