Django:货币是FloatField还是DecimalField?


76

我很好奇,哪种货币更适合作为货币领域?我将执行一些简单的操作,例如计算差异,新旧价格之间的百分比。我计划在零(即10.50)之后保留两位数字,并且大多数情况下如果这些数字为零,我将隐藏这些数字并将其显示为“ 10”

ps:货币不是基于美元的:)

Answers:


129

总是用DecimalField钱。即使是简单的运算(加法,减法)也无法避免浮点舍入问题:

>>> 10.50 - 0.20
10.300000000000001

>>> Decimal('10.50') - Decimal('0.20')
Decimal('10.30')

7
>>> 10.50 - 0.20在Python 2.7中获取10.3
Rockallite

6
哭,废话。我会将大量数据从FloatField字段/列中迁移到DecimalField ...
teewuane

时间到了,我们有一个“不要为了钱而使用浮点”类似于bobince的stackoverflow.com/a/1732454/604511
Jesvin Jose 2014年

@Rockallite-try1.00 + 0.14"%.15f" % (10.5 - 0.2)-由于(我假设)bugs.python.org/issue1580或类似问题,该问题可能会被掩盖。
塞斯(Seth)2015年

@teewuane在Django项目中将FloatField转换为DecimalField时遇到任何麻烦吗?我正在重构一些处理资金的老项目,但要在各处使用Float,OMG,Thx。
李梦龙

57

这个问题的答案是正确的,但是一些用户会迷惑于这个问题,以找出DecimalField和FloatField之间的区别。塞思提出的浮点取整问题是货币问题。

Django文档状态

FloatField类有时与DecimalField类混合在一起。尽管它们均表示实数,但它们以不同的方式表示这些数字。FloatField内部使用Python的float类型,而DecimalField使用Python的Decimal类型。在这里阅读更多。

以下是这两个字段之间的其他区别:

DecimalField:

  • DecimalFields必须定义“ decimal_places”和“ max_digits”属性。
  • 您从上面的必需属性中获得了两个包含在此处的自由格式验证,即,如果将max_digits设置为4,并且键入的小数位数是4.00000(5位数字),则会出现此错误:确保不超过4个总位数。
  • 您还可以对小数位进行类似的表单验证(在大多数浏览器中,也将使用输入字段中的step属性在前端进行验证。如果您设置decimal_places = 1并输入0.001作为值,则会出现错误最小值必须为0.1。
  • 返回一个十进制数,十进制,类型为 <class 'decimal.Decimal'>
  • 没有作为DecimalField的额外验证
  • 对于Decimal类型,由于需要如上所述设置必需的属性,因此还会为您处理舍入。所以从外壳上来说,如果您
  • 在数据库(postgresql)中,DecimalField被保存为数字(max_digits,decimal_laces)类型,并且Storage设置为“ main”,从上面的示例中,类型为numeric(4,1)

有关Django Docs中DecimalField的更多信息。

FloatField:

  • 返回内置的float类型, <type 'float'>
  • 没有智能舍入,并且实际上可能会导致如Seths答案中所述的舍入问题。
  • 没有从DecimalField获得的额外表单验证
  • 在数据库(postgresql)中,将FloatField保存为“双精度”类型,并将“存储”设置为“普通”

Django Docs中有关FloatField的更多信息。

两者都适用:

  • 这两个字段都从“ Field”类扩展,并且可以接受“空白”,“空”,“详细名称”,“名称”,“ primary_key”,“ max_length”,“唯一”,“ db_index”,“ rel”,“默认” ','可编辑','序列化','unique_for_date','unique_for_month','unique_for_year','choices','help_text','db_column','db_tablespace','auto_created','validators','error_messages'属性,因为所有从“字段”扩展的字段都将具有。
  • 这两个字段的默认表单小部件都是TextInput。

我在寻找两个领域之间的差异时遇到了这个问题,因此我认为这将对处于相同情况的人们有所帮助:)

更新:要回答这个问题,我认为您可以用两种货币来表示货币,尽管十进制更适合。当它计算为浮点数时存在一个舍入问题,因此必须使用round(value, 2)它才能使浮点表示形式舍入到两位小数位。这是一个简单的示例:

>>> round(1.13 * 50 + .01, 2)
56.51

您仍然会遇到浮动和舍入的麻烦。像这里一样,我们将其舍入为5的值:

>>> round(5.685, 2)
5.68

但是在这种情况下,它将四舍五入:

>>> round(2.995, 2)
3.0

这与浮点数在内存中的存储方式有关。看这里


似乎我将始终在FloatField上使用DecimalField。在选择舍入问题时,我看不到选择FloatField有任何好处/易于使用/简单性。
安坎-泽罗布

1
是的,如果您要处理一组有限的小数位数(例如货币),则为DecimalField。如果要使用浮点类型,请使用FloatField(可能是更罕见的用例)。
radtek

8

我知道这已经很老了,但是我偶然发现了它,寻找了完全不同的东西,我想抛出一个结论,通常不建议对货币使用浮点数(浮点数十进制数),因为浮点数会四舍五入总是会导致计算中的小错误,随着时间的流逝,这些小错误可能会导致很大的差异。

而是根据您的喜好使用整数字段或字符串。将您的货币乘以将小数点后移到最后并在存储时产生一个整数,然后在需要显示时将小数点后移到其所属的位置。基本上,这就是银行(和大多数货币库)处理存储数据的方式,以后可以为您省去很多麻烦。

我很难学到这一点,因为这并不是一个常见的话题。也许这可以使别人免于做同样的事情。


7

编辑:Satchmo项目不再处于活动状态,因此请看一下这些处理货币的替代方法


基于Django的Satchmo项目具有一个CurrencyField和CurrencyWidget,值得一看。

查看satchmo_utils应用程序目录以获取源代码

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.