你質疑的答案是說在總和期間使用更高的精度,但我不明白爲什麼。答案是正確的。考慮一個完全虛構的人數這個簡化的版本:
#include <iostream>
#include <iomanip>
float w = 0.;
float calcFloat(const int* origin, int n)
{
float d = 0;
for(int k = 0; k < n; k++)
d += origin[k] * w;
return (float)d;
}
float calcDouble(const int* origin, int n)
{
double d = 0;
for(int k = 0; k < n; k++)
d += origin[k] * w;
return (float)d;
}
int main()
{
int o[] = { 1111, 22222, 33333, 444444, 5555 };
std::cout << std::setprecision(9) << calcFloat(o, 5) << '\n';
std::cout << std::setprecision(9) << calcDouble(o, 5) << '\n';
}
的結果是:
6254.77979
6254.7793
因此,即使輸入是在兩種情況下是相同的,你使用double
了不同的結果中間求和。將calcDouble
更改爲使用(double)w
不會更改輸出。
這表明(origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w
的計算精度很高,但求和過程中的錯誤累積是他們試圖避免的。
這是因爲處理浮點數時錯誤如何傳播。引用The Floating-Point Guide: Error Propagation:
一般:
- 乘法和除法是「安全的」操作
- 加減法是很危險的,因爲當不同幅度的數目都參與其中,較小幅度的數字號碼丟失了。
所以你想要更高精度類型的總和,其中涉及加法。將整數乘以double
而不是float
幾乎沒有多大關係:您將得到的結果與開始時的值(與結果不是非常大或非常非常相近)小)。但總結float
值可能有非常不同的數量級,即使個別數字本身可表示爲float
,也會累積錯誤並偏離真實答案。
地看到,在行動:
float f1 = 1e4, f2 = 1e-4;
std::cout << (f1 + f2) << '\n';
std::cout << (double(f1) + f2) << '\n';
或等價,但更接近原始代碼:
float f1 = 1e4, f2 = 1e-4;
float f = f1;
f += f2;
double d = f1;
d += f2;
std::cout << f << '\n';
std::cout << d << '\n';
結果是:
10000
10000.0001
添加浮點數失去精確。即使輸入相同,將float添加到double也會給出正確的答案。您需要9位有效數字來表示正確的值,這對於float
來說太多了。
也許'f [k] .w'是一個'double'。 –
@ tobi303 ehm [nope](http://stackoverflow.com/questions/10108053/ranges-of-floating-point-datatype-in-c) – justHelloWorld
@FrançoisAndrieux等等呢? :)你可以總結兩個雙打,並將結果保存在一個沒有任何投球的浮球,對吧? – justHelloWorld