2012-04-29 62 views
7

我有一个问题,当我增加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 

为什么这些值出来有什么不同?

+0

您的示例代码在*交换性*,而不是*关联性*方面有所不同。证明相关性的版本将是'(0.7 +(0.1 + 0.2))' – 2014-06-22 20:50:47

+0

@MattMcNabb:+是二元运算。对于浮点操作数,它是可交换的,但不是关联的。因此,如果你有两个产生不同结果的表达式,你不能通过仅应用可交换性来形成另一个表达式。 – tmyklebu 2014-06-26 19:39:48

+0

@tmyklebu好吧,所以这确实会检查关联性,当且仅当已知交换性成立。 (C++标准似乎不能保证交换性)。 – 2014-06-27 01:27:28

回答

10

浮点加法不一定联想。如果你改变你添加的顺序,这可以改变结果。

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

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

5

什么是可能的,与目前流行的机器和软件,是:

编译器编码的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)。

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)

+3

或0%的时间,如果'RAND_MAX <100000'! – 2014-06-22 20:54:16