【问题标题】:Is float field + round() for currency fields in django OK?django 中货币字段的浮点字段 + round() 可以吗?
【发布时间】:2014-10-19 03:56:16
【问题描述】:
我在我的 django 项目中使用浮点字段作为货币是天真的。我刚刚了解了 python float 的这个舍入问题:
>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17
现在我有两个选择:
- 使用 South 将所有浮点字段迁移到十进制字段,这很痛苦。
- 在每次加减运算后执行 round() 以获得正确的结果,然后再保存。毕竟,我的应用程序中的所有货币字段都只允许小数点后一位。
我的问题是,如果我正在处理的只是加减 100.0 和 1.2 之类的数字,那么选项 2 可以吗?除了舍入误差还有什么需要担心的吗?
【问题讨论】:
标签:
python
django
floating-point
decimal
【解决方案1】:
小心round(); round(0.1+0.2-0.3+1.2) 会给你 1.0,而不是 1.2。您需要使用Decimal,然后将其正确转换为float:
>>> r = Decimal('0.1') + Decimal('0.2') - Decimal('0.3')
>>> r
Decimal('0.0')
>>> float(r)
0.0
>>> round(0.1+0.2-0.3+1.2)
1.0
>>> r = Decimal('0.1') + Decimal('0.2') - Decimal('0.3') + Decimal('1.2')
>>> r
Decimal('1.2')
>>> float(r)
1.2
这也将处理您必须手动处理的边缘情况,应该使用 float() + round() 方法。
最后,不要混淆值的打印方式和存储方式。 “只允许 1 个小数位” - 这是一个演示细节。
【解决方案2】:
使用decimal字段,使用decimal.Decimal,在Python中准确处理货币:
>>> from decimal import Decimal as D
>>> D('0.1') + D('0.2') - D('0.3')
Decimal('0.0')
>>> assert _ == 0
【解决方案3】:
需要明确的是,问题不在于 Python 的浮点数,而在于浮点数的二进制表示 - 对于易于用十进制表示的数字,它通常并不精确。
一般来说,我同意您应该使用Decimal 类的其他答案,因为它是为精确表示浮点数而设计的(以空间为代价+数学运算的效率)。另一种选择,如果你确切地知道你的精度是多少(如你所说,一次小数点)将使用整数作为定点数。您将 1.3 存储为 13 并且只知道在显示之前需要将其移动一位小数。但是使用 Decimal 更干净,代码可能更具可读性。