2012-04-29 99 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