2017-02-09 82 views
0

我正在努力提高surf.cpp的表现。从线140,就可以找到此功能:为什么使用double,然后转换为float?

inline float calcHaarPattern(const int* origin, const SurfHF* f, int n) 
{ 
    double d = 0; 
    for(int k = 0; k < n; k++) 
     d += (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w; 
    return (float)d; 
} 

运行Intel Advisor矢量分析,它表明“本1个数据类型转换”,这可能是低效的(特别是在矢量化)。

但是我的问题是:看这个函数,为什么作者会创建d作为double,然后把它投到float?如果他们想要一个十进制数,float就可以。我想到的唯一原因是,因为doublefloat更精确,所以它可以代表较小的数字,但最终值足够大,可以存储在float中,但我没有对d值执行任何测试。

任何其他可能的原因?

+1

也许'f [k] .w'是一个'double'。 –

+0

@ tobi303 ehm [nope](http://stackoverflow.com/questions/10108053/ranges-of-floating-point-datatype-in​​-c) – justHelloWorld

+0

@FrançoisAndrieux等等呢? :)你可以总结两个双打,并将结果保存在一个没有任何投球的浮球,对吧? – justHelloWorld

回答

7

因为作者希望在计算过程中具有更高的精度,那么只能围绕最终结果。这与在计算过程中保留更重要的数字相同。

更确切地说,当加法和减法时,可以累积误差。当涉及大量浮点数时,这个错误可能相当严重。

+0

这很奇怪。为什么他们不会在乘以整数之前将'f [k] .w'强制转换为'double' **。这样,代码可以利用总和的更高精度,但决定不考虑加数。这真的很奇怪。 – IInspectable

+0

似乎只有从1到n的积累提升了一倍。内循环中,这4个数字保持在较低的分辨率... –

+0

@IInspectable可能是因为它没有太大的区别?看我的答案中的例子。 –

4

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

#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来说太多了。

+0

*“将'calcDouble'更改为使用'(double)w'不会改变输出。”* - 公平地说,它不会更改输出,因为您随机选取的输入**。这远不是一个证明,我很抱歉。 – IInspectable

+1

我很抱歉,但你没有要求提供证据,我也没有声称要提供证明。如果你不明白为什么使用'double'来计算总和,你需要阅读浮点数和错误传播。我已经为你添加了一个参考。 –

+0

@Jonathan Wakely很好的例子和解释。 –

相关问题