浮点精度设置的工作原理

How Floating Point precision setting works

我已经看到很多关于如何为浮点数设置精度的答案,我们到处都在做类似下面的事情:

double RoundDouble(double doValue,int nPrecision)
{
    return (floor((doValue*pow(10,nPrecision)+0.5))/pow(10,nPrecision));
}

我不明白乘以和除以几乎相等的数字如何正确设置精度?谁能详细解释一下

让我们一步一步来。

  1. x = doValue * pow(10, nPrecision)nPrecision 位移到整数部分,其他留在分形部分;
  2. y = floor(x + 0.5) — 四舍五入到整数部分(如果 x 是非负数);
  3. z = y / pow(10, nPrecision) — 将 nPrecision 位移回分形部分。

上面我没有实现,但是如果我们使用一些示例输入调试它,那么会发生如下情况:

// say we have 5.89162 and we want it to 2 decimal places 5.89 so
RoundDouble(5.89162,2)
{
return (floor(5.89162*pow(10,2)+0.5))/pow(10,2);
/* which will look like
    floor((5.89162x100+0.5)/100)
     floor(589.662/100)
     floor(5.89662)
and floor function will bound it to 5 it means the output will be 5 instead of 5.89*/
}

这只是用整数四舍五入来实现我们在学校学过的截断小数点后的技巧:取小数点后的前N位,把最右边的向上四舍五入或向下。

如果您考虑将 12.3456 四舍五入到小数点后两位,您自然会期望结果为 12.35,因为“4”是两位数字中最右边的,并被“5”四舍五入接下来。

现在,为了用数学实现这一点,我们使用 floor 来实现舍入(实际上您可以使用 std::round)。但这会将我们带到一个整数,我们将丢失所有小数部分。

为了避免这种情况,我们首先乘以 100,将所有有趣的部分移到整数领域:

1234.56

如果用 std::floor(x+0.5)std::round(x) 将此数字四舍五入到最接近的整数值,则得到:

1235.0

最后,将其除以 100 得到四舍五入(是的,记住我们四舍五入)到小数点后两位的数字:

12.35

希望您现在了解调用 pow 的情况。通过提高 10 的 nPrecision 次方,我们得到一个比例因子,当使用这个技巧时,它会在四舍五入后提供那么多小数位。在这种情况下,我们想要 2,而 pow(10,2) 是 100。

为了便于阅读,我冒昧地清理了你的函数:

double RoundDouble(double doValue, int nPrecision)
{
    double scale_factor = pow(10.0, static_cast<double>(nPrecision));
    return std::round(doValue * scale_factor) / scale_factor;
}

没有。浮点数没有小数位。它有二进制位,它们与小数位不可通约。它所做的只是提供一个近似值。

证明参见here

如果你想要准确的小数位,你必须使用小数基数。