简体   繁体   English

浮点精度设置如何工作

[英]How Floating Point precision setting works

I have seen many answers on how to set precision for floating point numbers and everywhere we are doing something like below : 我已经看到了很多关于如何为浮点数设置精度的答案,并且在我们正在做的每件事中,如下所示:

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

I could not understand how multiplying and dividing by almost equal numbers will set precision correctly ? 我无法理解几乎相等的数字相乘和相除如何正确设置精度? can anybody explain in detail 谁能详细解释

Let's do it step-by-step. 让我们逐步进行。

  1. x = doValue * pow(10, nPrecision)nPrecision digits are shifted to integral part, others stay in fractal part; x = doValue * pow(10, nPrecision)nPrecision数字移到整数部分,其他数字留在分形部分;
  2. y = floor(x + 0.5) — round to integral part (if x is non-negative); y = floor(x + 0.5) -舍入为整数部分(如果x为非负数);
  3. z = y / pow(10, nPrecision) — shift nPrecision digits back to fractal part. z = y / pow(10, nPrecision) —将nPrecision数字移回分形部分。

I have not implemented above but If we debug it using the some sample input then something like below will happen: 我没有在上面实现,但是如果我们使用一些示例输入对其进行调试,则将发生以下类似情况:

// 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*/
}

This is just using whole-number rounding to achieve the trick that we all learned at school for cutting off at some number of decimal places: take the first N digits after the decimal, and round the right-most one up or down. 这只是使用整数舍入来实现我们在学校学习的技巧,即在小数点后一位数字上取整:取小数点后的前N个数字,然后将最右边的一位向上或向下取整。

If you think about this in rounding 12.3456 to 2 decimal places, you naturally expect the result to be 12.35, because the '4' is the right-most of the two digits and gets rounded up by the '5' that follows. 如果您考虑将这个数字四舍五入为12.3456到2位小数,您自然会期望结果为12.35,因为“ 4”是两位数的最右边,并被其后的“ 5”四舍五入。

Now, to achieve this with math we utilize floor to achieve the rounding (actually you can utilize std::round instead). 现在,要通过数学来实现这一点,我们利用floor进行舍入(实际上,您可以使用std::round代替)。 But that will take us to a whole number and we'll lose all the fractional part. 但这会将我们带到一个整数,我们将失去所有小数部分。

To avoid that, we first multiply by 100, moving all the interesting parts into the whole number realm: 为了避免这种情况,我们首先乘以100,将所有有趣的部分移到整数域中:

1234.56

If you round this number to the nearest whole value with either std::floor(x+0.5) or std::round(x) , you then get: 如果使用std::floor(x+0.5)std::round(x)将该数字四舍五入到最接近的整数值,则会得到:

1235.0

Finally, dividing this by 100 gives you the number rounded (yes, remember we rounded it) to two decimal places: 最后,将其除以100可得到四舍五入的数字(是的,请记住我们将其四舍五入)到小数点后两位:

12.35

Hopefully you now see what's going on with the call to pow . 希望您现在看到对pow的调用发生了什么。 By raising 10 to the power of nPrecision , we get a scaling factor that will provide that many decimal places after rounding, when using this trick. 通过将nPrecision的幂提高10,我们得到了一个比例因子,当使用此技巧时,该比例因子将在舍入后提供许多小数位。 In this case, we wanted 2, and pow(10,2) is 100. 在这种情况下,我们需要2,而pow(10,2)是100。

I took the liberty of cleaning up your function for readability: 我为了保证可读性而清理了您的函数:

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

It doesn't. 没有。 Floating point doesn't have decimal places. 浮点数没有小数位。 It has binary places, and they are incommensurable with decimal places. 它具有二进制位,并且它们与小数位是无法比拟的。 All it does is provide an approximation. 它所做的只是提供一个近似值。

For proof see here . 为了证明,请看这里

If you want decimal places accurately you have to use a decimal radix. 如果要精确地保留小数位,则必须使用十进制基数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM