浮点加法和乘法是否相关?

时间:2012-04-29 11:46:29

标签: c++ floating-point

当我添加三个浮点值并将它们与1进行比较时,我遇到了问题。

cout << ((0.7 + 0.2 + 0.1)==1)<<endl;     //output is 0
cout << ((0.7 + 0.1 + 0.2)==1)<<endl;     //output is 1

为什么这些价值会有所不同?

4 个答案:

答案 0 :(得分:14)

浮点加法不一定是关联的。如果您更改添加内容的顺序,则可能会更改结果。

关于这个问题的标准文件是What Every Computer Scientist Should Know about Floating Point Arithmetic。它给出了以下示例:

  

另一个灰色区域涉及括号的解释。由于舍入误差,代数的关联定律不一定适用于浮点数。例如,当x = 1e30,y = -1e30且z = 1时,表达式(x + y)+ z与x +(y + z)的答案完全不同(前者为1,后者为0) )。

答案 1 :(得分:7)

目前流行的机器和软件可能是:

编译器将.7编码为0x1.6666666666666p-1(这是十六进制数字1.6666666666666乘以2得-1的幂),.2编码为0x1.999999999999ap-3,.1 1}}为0x1.999999999999ap-4。这些中的每一个都是浮点数可表示的数字,它最接近您所写的十进制数字。

观察到这些十六进制浮点常数中的每一个在其有效数字中都恰好有53位(&#34;分数&#34;部分,通常不准确地称为尾数)。有效数字的十六进制数字具有&#34; 1&#34;对于64位二进制浮点数,还有13个十六进制数字(每个四位,总共52位,53包括&#34; 1&#34;),这是IEEE-754标准提供的。

让我们添加.7.2的数字:0x1.6666666666666p-1和0x1.999999999999ap-3。首先,缩放第二个数字的指数以匹配第一个数字。为此,我们将指数乘以4(改变&#34; p-3&#34;到&#34; p-1&#34;)并将有效数乘以1/4,得到0x0.66666666666668p-1 。然后添加0x1.6666666666666p-1和0x0.66666666666668p-1,给出0x1.ccccccccccccc8p-1。注意,这个数字在有效数字中有超过53位:&#34; 8&#34;是期后的第14位数。浮点数不能返回具有这么多位的结果,因此必须将其四舍五入到最接近的可表示数字。在这种情况下,有两个数字同样接近,0x1.cccccccccccccp-1和0x1.ccccccccccccdp-1。当存在平局时,使用有效数字的最低位中具有零的数字。 &#34; C&#34;是偶数和&#34; d&#34;很奇怪,所以&#34; c&#34;用来。添加的最终结果是0x1.cccccccccccccp-1。

接下来,将.1(0x1.999999999999ap-4)的编号添加到该编号。同样,我们扩展以使指数匹配,因此0x1.999999999999ap-4变为0x.33333333333334p-1。然后将其添加到0x1.cccccccccccccp-1,给出0x1.fffffffffffff4p-1。舍入到53位给出0x1.fffffffffffffp-1,这是.7+.2+.1的最终结果。

现在考虑.7+.1+.2。对于.7+.1,添加0x1.6666666666666p-1和0x1.999999999999ap-4。回想一下后者缩放到0x.33333333333334p-1。那么确切的总和是0x1.99999999999994p-1。舍入到53位给出0x1.9999999999999p-1。

然后添加.2(0x1.999999999999ap-3)的数字,该数字缩放为0x0.66666666666668p-1。确切的总和是0x2.00000000000008p-1。浮点有效数字总是按1开始缩放(特殊情况除外:可表示范围底部的零,无穷大和非常小的数字),因此我们将其调整为0x1.00000000000004p0。最后,我们舍入到53位,给出0x1.0000000000000p0。

因此,由于舍入时出现错误,.7+.2+.1返回0x1.fffffffffffffp-1(略小于1),.7+.1+.2返回0x1.0000000000000p0(正好为1)。

答案 2 :(得分:2)

浮点乘法在C或C ++中不是关联的。

证明:

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
using namespace std;
int main() {
    int counter = 0;
    srand(time(NULL));
    while(counter++ < 10){
        float a = rand() / 100000;
        float b = rand() / 100000;
        float c = rand() / 100000;

        if (a*(b*c) != (a*b)*c){
            printf("Not equal\n");
        }
    }
    printf("DONE");
    return 0;
}

在此计划中,约有30%的时间(a*b)*c不等于a*(b*c)

答案 3 :(得分:0)

与 Eric 的类似答案,但用于添加和 Python。

import random

random.seed(0)
n = 1000
a = [random.random() for i in range(n)]
b = [random.random() for i in range(n)]
c = [random.random() for i in range(n)]

sum(1 if (a[i] + b[i]) + c[i] != a[i] + (b[i] + c[i]) else 0 for i in range(n))
相关问题