简体   繁体   English

Python十进制格式错误的行为?

[英]Python Decimal formatting wrong behavior?

I'm trying to obtaining a coherent value when formatting a Decimal with '%.2f'. 我在尝试使用'%。2f'格式化Decimal时尝试获取一致值。 However the results are surprising to me. 然而,结果令我感到惊讶。 Same decimal value, different rounding. 相同的十进制值,不同的舍入。 Any idea what I'm doing wrong ? 知道我做错了什么吗?

>>> from decimal import Decimal
>>> x = Decimal('111.1650')
>>> print '%.2f' % x
111.17
>>> y = Decimal('236.1650')
>>> print '%.2f' % y
236.16

Thank you 谢谢

Python's percent-style formatting doesn't understand Decimal objects: when you format, there's an implicit conversion to float. Python的百分比样式格式化不理解Decimal对象:格式化时,隐式转换为float。 It just so happens that the nearest representable binary float to your x value is this: 碰巧的是,最接近可表示的二进制浮点数到你的x值是这样的:

>>> print Decimal(float(x))
111.1650000000000062527760746888816356658935546875

That's a touch greater than the 111.165 halfway case, so it rounds up. 这是一个比111.165中途案件更大的触摸,因此它会向上111.165 Similarly, for y , the value that ends up being formatted is this one: 同样,对于y ,最终格式化的值是这样的:

>>> print Decimal(float(y))
236.164999999999992041921359486877918243408203125

In that case, the value being formatted for output is just below the halfway value, so it rounds down. 在这种情况下,为输出格式化的值恰好低于中间值,因此它向下舍入。

To avoid the implicit conversion, you can use the .format formatting method: 要避免隐式转换,可以使用.format格式化方法:

>>> "{0:.2f}".format(Decimal('111.1650'))
'111.16'
>>> "{0:.2f}".format(Decimal('236.1650'))
'236.16'

Note that you still might not like all the results, though: 请注意,您仍然可能不喜欢所有结果:

>>> "{0:.2f}".format(Decimal('236.1750'))
'236.18'

This style of formatting uses the round-half-to-even rounding mode by default. 默认情况下,这种格式化使用舍入到半舍入到舍入模式。 In fact, it takes the rounding mode from the current Decimal context, so you can do this: 实际上,它从当前的Decimal上下文采用舍入模式,因此您可以这样做:

>>> from decimal import getcontext, ROUND_HALF_UP
>>> getcontext().rounding=ROUND_HALF_UP
>>> "{0:.2f}".format(Decimal('236.1750'))
'236.18'
>>> "{0:.2f}".format(Decimal('236.1650'))  # Now rounds up!
'236.17'

As a general comment, being able to implement custom formatting for user-defined classes is one of the big wins for the new-style .format formatting method over the old-style % -based formatting. 作为一般性评论,能够为用户定义的类实现自定义格式是新式.format格式化方法相对于旧式基于%的格式的一个重大胜利。

Your problem is repeatable. 你的问题是可重复的。 As a workaround, you could use the newer libraries. 作为解决方法,您可以使用较新的库。

    >>> '{0:.2f}'.format( Decimal('236.1650') )
    '236.16'
    >>> '{0:.2f}'.format( Decimal('111.1650') )
    '111.16'

This happens because float values cannot be represented exactly. 发生这种情况是因为无法准确表示浮点值。 Refer to the documentation floating point arithmetic. 请参阅文档浮点运算。

Or, to give you a clue as to what happens, enter: 或者,为了告诉您发生了什么,请输入:

>>> Decimal(111.1650)
Decimal('111.1650000000000062527760746888816356658935546875')
>>> Decimal(236.1650)
Decimal('236.164999999999992041921359486877918243408203125')

The problem is that the floating point numbers are not exact near the .005 boundary in your case. 问题是在您的情况下浮点数不准确接近.005边界。 You just met the case that one number will be slightly above .005 (therefore ceiled) and the other number is slightly below and gets floored. 你刚刚遇到的情况是,一个数字略高于.005(因此被剔除)而另一个数字稍微低于并且被淹没。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM