为什么使用double然后使用cast来浮动?

时间:2017-02-09 19:33:28

标签: c++ casting floating-point double precision

我正在努力改善surf.cpp表演。从第140行开始,您可以找到此功能:

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值进行任何测试。

还有其他可能的原因吗?

2 个答案:

答案 0 :(得分:7)

因为作者想要在计算过程中获得更高的精度,所以只能围绕最终结果。这与在计算过程中保留更多有效数字相同。

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

答案 1 :(得分:4)

你质疑答案是说在总结期间使用更高的精确度,但我不明白为什么。答案是对的。考虑这个带有完全编号的简化版本:

#include <iostream>
#include <iomanip>

float w = 0.012345;

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,也会累积错误并进一步偏离真实答案。

要看到这一点:

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也能给出正确的答案。您需要九位有效数字来表示正确的值,并且对float来说太多了。