乘以浮点数和 keep/get 双精度精度

Multiplying floats and keep/get double precision accuracy

我有一个接受浮点数的函数,我正在用它们做一些计算,我想在 returned 结果中保持尽可能多的准确性。我读到,当您将两个浮点数相乘时,有效数字的数量就会翻倍。

因此,当两个浮点数相乘时,例如 float e, f; 而我做 double g = e * f,这些位何时被截断?

在我下面的示例函数中,我是否需要强制转换,如果需要,在哪里?这是一个紧密的内部循环,如果我将 static_cast<double>(x) 放在每个使用它的变量 a b c d 周围,我会减速 5-10%。但我怀疑我不需要单独转换每个变量,而且只需要在某些位置转换,如果有的话?或者 return 在这里加倍不会给我任何收益,我也可以 return 一个浮点数?

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

意义比减速 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);
}

我会选择它,即使它有点慢,因为它表达了你想要在计算中很好地实现双精度并且只需要接口上的浮点数的想法;同时它使您的函数体与转换分开(后者一步完成)。

当您将两个浮点数相乘而不进行转换时,结果将以浮点精度计算(即截断),然后转换为双精度。

要以双精度计算结果,您需要先将至少一个操作数转换为双精度。然后整个计算将以双精度完成(并且所有浮点值都将被转换)。然而,这将造成同样的放缓。减速可能是因为将数字从浮点数转换为双精度数并非完全微不足道(不同的位大小以及指数和尾数的范围)。

如果我这样做并控制了函数定义,我会将所有参数作为双精度传递(我通常在任何地方都使用双精度,在现代计算机上,float 与 double 计算之间的速度差异可以忽略不计,唯一的问题可能是在对大型值数组进行操作时的内存吞吐量和缓存性能)。

顺便说一句。对精度重要的情况实际上不是乘法,而是 addition/subtraction - 这就是精度可以产生很大差异的地方。考虑 adding/subtracting 1e+6 和 1e-3。