你质疑的答案是说在总和期间使用更高的精度,但我不明白为什么。答案是正确的。考虑一个完全虚构的人数这个简化的版本:
#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