浮点数相乘并保持/获得双精度精度

6
我有一个函数,需要使用浮点数进行计算,并且希望在返回结果时保留尽可能多的精度。我了解到,当你将两个浮点数相乘时,会增加有效数字的位数。
因此,当两个浮点数相乘时,比如float e, f; 并且我执行double g = e * f时,位数何时被截断呢?
在我的示例函数中,我是否需要强制转换?如果需要,在哪里进行转换?由于这是一个紧密的内部循环,如果我在每个变量a b c d使用时都加上static_cast<double>(x), 我会得到5-10% 的减速。但我怀疑我不需要单独转换每个变量,只需在某些位置上转换即可(如果需要)。或者在这里返回double是否会给我任何好处,我同样可以返回float?
double func(float a, float b, float c, float d) {
    return (a - b) * c + (a - c) * b;
}

1
你只需要一个强制类型转换,因为其他操作数将自动转换。"如果任一操作数是double,则另一个操作数将被转换为double"。但是这仍然会给你相同的减速效果。关于返回double的第二个问题取决于您对返回结果的处理方式。 - Richard Critten
是的,但在我的情况下,两个(或多个)操作数(除了返回类型)都是浮点数? - Ela782
1
如果您在代码中始终使用 double,可能会获得更好的性能(无论如何也会获得更好的准确性)。这样可以节省转换开销。 - 5gon12eder
@5gon12eder - 如果你在所有地方都使用double,你会获得更好的性能(而且无论如何也会更准确)...不要忘记这个“以RAM消耗为代价”的价格标签。 - Adrian Colomitchi
2个回答

7
当你在不进行类型转换的情况下乘以两个浮点数时,结果会以浮点精度计算(即截断),然后转换为双精度。
要在双精度中计算结果,您需要先将至少一个操作数转换为双精度。然后整个计算将在双精度中完成(并且所有浮点值都将被转换)。但是,这样做会导致相同的减速。减速可能是因为从浮点数到双精度数的转换不是完全平凡的(指数和幂次的位数和范围不同)。
如果我正在这样做并且控制着函数定义,我会将所有参数作为双精度传递(我通常在所有地方使用双精度,在现代计算机上在浮点数与双精度数之间计算的速度差异可以忽略不计,唯一的问题可能是在大量值的数组操作时出现的内存吞吐量和高速缓存性能问题)。
顺便说一句,对于精度而言,重要的实际上不是乘法,而是加法/减法——那是精度可以产生很大差异的地方。考虑添加/减去1e+6和1e-3。

谢谢,你的前三个句子完美回答了我的问题!而其他信息也非常有用。 - Ela782

4

意义比5-10%的减速更重要。我的做法:

double func_impl(double a, double b, double c, double d) {
    return (a - b) * c + (a - c) * b;
}

double func(float a, float b, float c, float d) {
    return func_impl(a, b, c, d);
}

即使它速度稍慢,我也会选择这个选项,因为它很好地表达了你想要在计算中使用双精度,并且只需要在接口上使用浮点数;同时,它将函数的主体与类型转换分开(后者一步完成)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接