我知道浮点数在大多数编程语言中都不是100%准确,但我刚才遇到了一个奇怪的问题。我还在学习Python,所以制作了一个简单的程序来计算尽可能少的硬币给出的变化。然而,当它达到0.02时,似乎无法给出2p硬币,而是将其分成2个1p硬币。代码段如下所示:
....
elif amountLeft / 0.02 >= 1:
changeGiven.append("2p")
amountLeft -= 0.02
else:
changeGiven.append("1p")
amountLeft -= 0.01
我在http://www.pythontutor.com中看过它,0.02
中的amountLeft
显然print 0.02 / 0.02 >= 1
在最后一次迭代中会减少到那个。当我检查True
时,我按预期返回{{1}}。
我在这里错过了什么明显的事情?
答案 0 :(得分:6)
因为你知道浮点数不是100%准确,所以你不应该发现0.02不能完全表示为Python浮点数。它实际上存储的值略高于0.02,如果以非常高的精度打印该值,您可以看到:
>>> print '{0:.32f}'.format(0.02)
0.02000000000000000041633363423443
当您不断从变量中减去0.02时,会产生这个小错误。这是一个从1.0开始的例子来说明我在说什么:
>>> x = 1.0
>>> for i in range(49):
... x -= 0.02
...
>>> x
0.019999999999999383
>>> x / 0.02 >= 1
False
要避免此舍入错误,请使用decimal模块而不是浮点数:
>>> from decimal import Decimal
>>> x = Decimal('1.0')
>>> for i in range(49):
... x -= Decimal('0.02')
...
>>> x
Decimal('0.02')
>>> x / Decimal('0.02') >= 1
True
或者,将所有值乘以100,使得减去整数2而不是浮点数0.02,这也可以避免舍入误差。
答案 1 :(得分:3)
首先,amountLeft / 0.02 >= 1
与amountLeft >= 0.02
大致相同(假设amountLeft
不是负数),而且更简单。
使用整数运算(直接使用便士,可以得到准确的结果,但在显示结果时必须手动添加.
:
from Decimal import decimal
amountLeft = round(amountLeft*100)
....
elif amountLeft >= 2:
changeGiven.append("2p")
amountLeft -= 2
else:
changeGiven.append("1p")
amountLeft -= 1
如果您确实需要一个程序以精确的方式处理小数,请使用十进制模块。假设输入是浮点数:
# Assume amountLeft contains a floating point number (e.g. 1.99)
# 2 is the number of decimals you need, the more, the slower. Should be
# at most 15, which is the machine precision of Python floating point.
amountLeft = round(Decimal(amountLeft),2)
....
# Quotes are important; else, you'll preserve the same errors
# produced by the floating point representation.
elif amountLeft >= Decimal("0.02"):
changeGiven.append("2p")
amountLeft -= Decimal("0.02")
else:
changeGiven.append("1p")
amountLeft -= Decimal("0.01")
答案 2 :(得分:-2)
欢迎来到浮点。 0.02 / 0.02不一定等于1.我最好的建议是始终对所有事物使用整数运算。我整天做科学编程,但我还没有发现需要浮点的问题。让我重申一点:为了清楚起见:您认为可以通过浮点运算解决的任何问题都可以通过整数运算更有效地解决。我能想到的唯一例外是当你需要使用只接受浮点输入的库时。
如果您坚持使用浮点,则需要使用舍入功能。
---------------------或FJ建议的十进制图书馆。