当我将三个浮点数相加并将它们与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
为什么这些值会有不同的输出?
当我将三个浮点数相加并将它们与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
为什么这些值会有不同的输出?
浮点数加法不一定是结合律的。如果您改变相加顺序,结果可能会发生改变。
该主题的标准文献为《计算机科学家应知道的浮点运算》。它提供了以下示例:
另一个灰色领域涉及括号的解释。由于舍入误差,代数的结合律不一定适用于浮点数。例如,当 x = 1e30、y = -1e30 和 z = 1 时,表达式 (x+y)+z 的答案与 x+(y+z) 的答案完全不同(前者为 1,后者为 0)。
.7
编码为0x1.6666666666666p-1(十六进制数1.6666666666666乘以2的-1次方),.2
编码为0x1.999999999999ap-3,.1
编码为0x1.999999999999ap-4。其中每个都是浮点数中可表示的最接近你输入的十进制数值的数字。.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)。#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;
}
(a*b)*c
不等于a*(b*c)
。RAND_MAX < 100000
,则有0%的几率。 - M.MRAND_MAX < 100000
,那么 rand() / 100000 == 0
,所以 !=
测试永远不会失败。 - M.MIEEE 743双精度(64位)数字既不支持加法结合律,也不支持乘法结合律。以下是每种情况的示例(使用Python 3.9.7计算):
>>> (.1 + .2) + .3
0.6000000000000001
>>> .1 + (.2 + .3)
0.6
>>> (.1 * .2) * .3
0.006000000000000001
>>> .1 * (.2 * .3)
0.006
和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))
(0.7 + (0.1 + 0.2))
。 - M.M