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

Kar*_*yan 10 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
Run Code Online (Sandbox Code Playgroud)

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

NPE*_*NPE 16

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

关于这个主题的标准论文是每个计算机科学家应该知道的浮点运算.它给出了以下示例:

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


Eri*_*hil 7

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

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

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

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


Eri*_*ski 5

浮点乘法在 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;
}
Run Code Online (Sandbox Code Playgroud)

在此程序中,大约 30% 的时间(a*b)*c不等于a*(b*c)

  • 或者如果“RAND_MAX &lt; 100000”则为 0% 的时间! (6认同)