在Django中,浮点字段加round()用于货币字段可以吗?

0 投票
3 回答
637 浏览
提问于 2025-04-28 08:40

我在我的Django项目中使用浮点数来处理货币,真是太天真了。我刚刚了解到Python浮点数的舍入问题:

>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17

现在我有两个选择:

  1. 用South把所有的浮点字段迁移到十进制字段,这个过程会很麻烦。
  2. 在每次加法和减法后使用round()函数,这样可以在保存之前得到正确的结果。毕竟,我的应用中所有的货币字段只允许保留一位小数。

我的问题是,如果我只是在处理像100.0和1.2这样的加减法,选择第二个方案可以吗?除了舍入错误,还有其他需要担心的地方吗?

暂无标签

3 个回答

1

首先,问题不在于Python的浮点数,而是在于浮点数的二进制表示——对于那些在十进制中容易表示的数字,它的表示往往不够准确。

一般来说,我同意其他回答的观点,建议你使用Decimal类,因为它是为了精确表示浮点数而设计的(虽然这样会占用更多的空间,并且数学运算的效率会降低)。另外一个选择是,如果你确切知道你的精度要求(就像你说的,小数点后保留一位),可以使用整数作为定点数。比如,你可以把1.3存储为13,然后在显示之前记得把它向右移动一位小数。但使用Decimal会更简洁,代码也更容易阅读。

1

在Python中,使用decimal类型来处理货币,这样可以确保计算的准确性。具体来说,就是用decimal.Decimal这个工具。

>>> from decimal import Decimal as D
>>> D('0.1') + D('0.2') - D('0.3')
Decimal('0.0')
>>> assert _ == 0
1

使用 round() 时要小心;比如 round(0.1+0.2-0.3+1.2) 的结果是 1.0,而不是 1.2。你需要使用 Decimal,然后再正确转换成浮点数:

>>> 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() 的方法,就得手动处理这些特殊情况。

最后,不要把值的显示方式和它的存储方式搞混。“只允许一个小数位”——这只是一个显示上的细节。

撰写回答