我有一个问题,当我增加3浮点值,并将它们与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
为什么这些值出来有什么不同?
我有一个问题,当我增加3浮点值,并将它们与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
为什么这些值出来有什么不同?
浮点加法不一定联想。如果你改变你添加的顺序,这可以改变结果。
关于这个问题的标准文件是What Every Computer Scientist Should Know about Floating Point Arithmetic。它给出以下示例:
另一个灰色区域涉及圆括号的解释。由于舍入误差,代数的联合定律不一定适用于浮点数。例如,当x = 1e30,y = -1e30和z = 1时,表达式(x + y)+ z与x +(y + z)具有完全不同的答案(前者为1,后者为0 )。
什么是可能的,与目前流行的机器和软件,是:
编译器编码的0.7作为0x1.6666666666666p-1(这是十六进制数字1.6666666666666乘以2的次方-1 ),.2为0x1.999999999999ap-3,.1为0x1.999999999999ap-4。这些中的每一个都是可以用浮点数表示的数字,与您写的十进制数字最接近。
观察到这些十六进制浮点常量中的每一个在它的有效位数(“分数”部分,通常不准确地称为尾数)中正好有53位。对于64位二进制浮点数字,有效位的十六进制数字有一个“1”和十三个多位十六进制数字(每个四位,52位总数,53位,包括“1”),这是IEEE-754标准规定的。点数。
让我们添加.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。回想一下后者的缩放比例为0x3333333333334p-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)。
浮点乘法在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)
。
或0%的时间,如果'RAND_MAX <100000'! – 2014-06-22 20:54:16
您的示例代码在*交换性*,而不是*关联性*方面有所不同。证明相关性的版本将是'(0.7 +(0.1 + 0.2))' – 2014-06-22 20:50:47
@MattMcNabb:+是二元运算。对于浮点操作数,它是可交换的,但不是关联的。因此,如果你有两个产生不同结果的表达式,你不能通过仅应用可交换性来形成另一个表达式。 – tmyklebu 2014-06-26 19:39:48
@tmyklebu好吧,所以这确实会检查关联性,当且仅当已知交换性成立。 (C++标准似乎不能保证交换性)。 – 2014-06-27 01:27:28