[Python-3000] Rounding in Py3k

Nick Coghlan ncoghlan at iinet.net.au
Thu Aug 3 14:58:44 CEST 2006


Some musings inspired by the rounding discussion on python-dev.

The Decimal module provides all of the rounding modes from the general decimal 
arithmetic specification [1].

Both Decimal rounding methods (quantize() and to_integral()) return Decimal 
instances - a subsequent explicit conversion to int() is needed if you want a 
real integer (just like the builtin round()).

Normal floats, OTOH, only have easy access to truncate (through int()) and 
round-half-up (through round()).

Additionally, the Decimal 'quantize' method signature is fine if you have 
decimal literals, but not so good for Python where you have to write 
"n.quantize(d('1e-2'))" to round to two decimal places.

The implicit Decimal->float conversion also allows Decimals to be rounded with 
the round() builtin, but that can lead to errors in rounding near the limits 
of floating point precision due to the use of an imprecise conversion in 
Decimal.__float__():

 >>> n = (1 + d("5e-16"))
 >>> n
Decimal("1.0000000000000005")
 >>> float(n.quantize(d('1e-15')))
1.0
 >>> round(n, 15)
1.0000000000000011

Would it be worthwhile to design a common rounding mechanism that can be used 
to cleanly round values to the built in floating point type, as well as being 
able to access the different rounding modes for decimal instances?

For example, replace the builtin function round() with a non-instantiable 
class like the following:

   _TEN = decimal.Decimal(10)
   class round(object):

     @staticmethod
     def half_up(num, ndigits=0):
         if isinstance(num, decimal.Decimal):
             return float(num.quantize(_TEN**(-ndigits)),
                          rounding = decimal.ROUND_HALF_UP)
         return float(num)._round_half_up()


     __call__ = half_up

     @staticmethod
     def down(num, ndigits=0):
         if isinstance(num, decimal.Decimal):
             return float(num.quantize(_TEN**(-ndigits)),
                          rounding = decimal.ROUND_DOWN)
         return float(num)._round_down()

     # etc for the other 5 rounding modes

Cheers,
Nick.

[1] The 7 decimal rounding modes:

round-down (truncate; round towards 0)
round-half-up (school rounding)
round-half-even (bankers' rounding)
round-ceiling (round towards positive infinity)
round-floor (round towards negative infinity)
round-half-down (WTF rounding :)
round-up (round away from zero)


-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://www.boredomandlaziness.org


More information about the Python-3000 mailing list