Python 舍入数字不准确

Mer*_*fan 1 rounding python-3.x

我有这两个值需要四舍五入到两位小数

n = 59.9250
n1 = 459.4250

print(round(n, 2))
print(round(n1, 2))
Run Code Online (Sandbox Code Playgroud)

输出

59.92
459.43
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么吗?有一种方法可以像我使用 python 版本 3.6.8 一样对两者进行舍入

pax*_*blo 5

Python 对内置浮点对象使用 IEEE-754 双精度数字,这意味着绝大多数数字无法精确表示。您可以通过创建一个低于和高于限制的变量来进行实验测试:

\n
>>> n3 = 1e308 ; n3\n1e+308\n>>> n3 = 1e309 ; n3\ninf\n
Run Code Online (Sandbox Code Playgroud)\n

但是,由于 CPython 是开源的,您还可以通过查看处理浮点对象的代码来验证这一点。在 CPython 中,您可以在其中找到它,Objects/floatobject.c并且您将看到代码中充满了用于double保存值的 C 变量。

\n
\n

因此,如果您将这两个数字输入转换站点(例如此二进制转换器),您将看到它们转换为最接近的匹配项,如下所示:

\n
                   |\n 59.925       59.92499999.....something\n459.925      459.42500000..1..something\n                   |\n
Run Code Online (Sandbox Code Playgroud)\n

您可以看到,遵循“下一个数字0..4向下舍入,向上5..9舍入”的规则,第一个数字将向下舍入59.92,因为下一个数字是4(请记住,这里没有特殊的“中途”行为发挥作用)因为两者都不完全是一半)。

\n

出于同样的原因,第二个数字将向上舍459.93,因为它的下一位数字是5

\n

Python 文档round实际上指出了这种可能性:

\n
\n

注意: for floats 的行为round()可能会令人惊讶:例如,round(2.675, 2)给出2.67而不是预期的2.68。这不是一个错误:它是大多数小数不能精确表示为浮点数的结果。有关更多信息,请参阅浮点算术:问题和限制。

\n
\n
\n

如果您确实想避免此问题,则应该跳过使用内置浮点对象及其所有缺点,而选择使用该类Decimal

\n

然后,您可以用多种方式操作数字,同时避免精度损失。例如,以下文字记录:

\n
    \n
  • 创建精确值(不是一些最接近的浮点匹配);
  • \n
  • 更改上下文,以便将半值舍入到远离零的位置(而不是像您从说明符中想到的那样向上舍入到无穷大ROUND_HALF_UP),而不是使用银行家舍入;然后
  • \n
  • 将值四舍五入到小数点后两位
  • \n
\n
>>> d = decimal.Decimal("59.925")\n>>> decimal.getcontext().rounding = decimal.ROUND_HALF_UP\n>>> round(d, 2)\nDecimal(\'59.93\')\n
Run Code Online (Sandbox Code Playgroud)\n