为什么使用 double 然后转换为 float?
Why using double and then cast to float?
我正在努力提高 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
就可以了。我想到的唯一原因是,由于 double
比 float
更精确,因此它可以表示更小的数字,但最终值足够大,可以存储在 float
中,但我没有 运行 对 d
值进行任何测试。
还有其他可能的原因吗?
因为作者在计算的时候想要精度更高,所以只对最后的结果进行了四舍五入。这与在计算过程中保留更多有效位相同。
更确切地说,加减法时,误差是可以累积的。当涉及大量浮点数时,此错误可能相当大。
你质疑的答案是在求和时使用更高的精度,但我不明白为什么。这个答案是正确的。考虑这个带有完整 made-up 个数字的简化版本:
#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
的计算是 high-enough 精度,但他们试图避免在求和过程中累积误差。
这是因为在处理浮点数时错误是如何传播的。引用 The Floating-Point Guide: Error Propagation:
In general:
- Multiplication and division are “safe” operations
- Addition and subtraction are dangerous, because when numbers of different magnitudes are involved, digits of the smaller-magnitude number are lost.
因此您需要 higher-precision 类型的总和,这涉及加法。将整数乘以 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
.
来说太多了
我正在努力提高 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
就可以了。我想到的唯一原因是,由于 double
比 float
更精确,因此它可以表示更小的数字,但最终值足够大,可以存储在 float
中,但我没有 运行 对 d
值进行任何测试。
还有其他可能的原因吗?
因为作者在计算的时候想要精度更高,所以只对最后的结果进行了四舍五入。这与在计算过程中保留更多有效位相同。
更确切地说,加减法时,误差是可以累积的。当涉及大量浮点数时,此错误可能相当大。
你质疑的答案是在求和时使用更高的精度,但我不明白为什么。这个答案是正确的。考虑这个带有完整 made-up 个数字的简化版本:
#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
的计算是 high-enough 精度,但他们试图避免在求和过程中累积误差。
这是因为在处理浮点数时错误是如何传播的。引用 The Floating-Point Guide: Error Propagation:
In general:
- Multiplication and division are “safe” operations
- Addition and subtraction are dangerous, because when numbers of different magnitudes are involved, digits of the smaller-magnitude number are lost.
因此您需要 higher-precision 类型的总和,这涉及加法。将整数乘以 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
.