Python Decimal formatting misbehavior?

I am trying to get a consistent value when formatting a decimal with "% .2f". However, the results surprise me. The 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 

thanks

+4
source share
3 answers

Percentage formatting in the Python style does not understand Decimal objects: when formatting, an implicit conversion to float occurs. It so happens that the closest binary binary float to your x value is:

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

This touch is larger than 111.165 halfway, so it is rounded. Similarly, for y value that ends with formatting is as follows:

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

In this case, the value formatted for the output is slightly lower than the halfway value, so it is rounded down.

To avoid implicit conversion, you can use the .format formatting .format :

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

Please note that you may still not like all the results:

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

This formatting style uses a rounded rounded rounding mode by default. In fact, it takes a rounding mode from the current Decimal context, so you can do this:

 >>> 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 note, the ability to implement custom formatting for custom classes is one of the big gains for the new .format formatting method compared to the old-style % formatting.

+5
source

Your problem is repeatable. As a workaround, you can use newer libraries.

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

This is because float values โ€‹โ€‹cannot be represented exactly. See the documentation for floating point arithmetic.

Or, to give you an idea of โ€‹โ€‹what is going on, type:

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

The problem is that floating point numbers are not accurate near the .005 border in your case. You just met the case that one number will be slightly higher .005 (therefore, it will be closed), and the other number will be slightly lower and will be blocked.

+2
source

Source: https://habr.com/ru/post/1494805/


All Articles