我有一個問題,當我增加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