Question

Is there a way to get the ceil of a high precision Decimal in python?

>>> import decimal;
>>> decimal.Decimal(800000000000000000001)/100000000000000000000
Decimal('8.00000000000000000001')
>>> math.ceil(decimal.Decimal(800000000000000000001)/100000000000000000000)
8.0

math rounds the value and returns non precise value

Was it helpful?

Solution

x = decimal.Decimal('8.00000000000000000000001')
with decimal.localcontext() as ctx:
    ctx.prec=100000000000000000
    ctx.rounding=decimal.ROUND_CEILING
    y = x.to_integral_exact()

OTHER TIPS

The most direct way to take the ceiling of a Decimal instance x is to use x.to_integral_exact(rounding=ROUND_CEILING). There's no need to mess with the context here. Note that this sets the Inexact and Rounded flags where appropriate; if you don't want the flags touched, use x.to_integral_value(rounding=ROUND_CEILING) instead. Example:

>>> from decimal import Decimal, ROUND_CEILING
>>> x = Decimal('-123.456')
>>> x.to_integral_exact(rounding=ROUND_CEILING)
Decimal('-123')

Unlike most of the Decimal methods, the to_integral_exact and to_integral_value methods aren't affected by the precision of the current context, so you don't have to worry about changing precision:

>>> from decimal import getcontext
>>> getcontext().prec = 2
>>> x.to_integral_exact(rounding=ROUND_CEILING)
Decimal('-123')

By the way, in Python 3.x, math.ceil works exactly as you want it to, except that it returns an int rather than a Decimal instance. That works because math.ceil is overloadable for custom types in Python 3. In Python 2, math.ceil simply converts the Decimal instance to a float first, potentially losing information in the process, so you can end up with incorrect results.

You can do this using the precision and rounding mode option of the Context constructor.

ctx = decimal.Context(prec=1, rounding=decimal.ROUND_CEILING)
ctx.divide(decimal.Decimal(800000000000000000001), decimal.Decimal(100000000000000000000))

EDIT: You should consider changing the accepted answer.. Although the prec can be increased as needed, to_integral_exact is a simpler solution.

>>> decimal.Context(rounding=decimal.ROUND_CEILING).quantize(
...   decimal.Decimal(800000000000000000001)/100000000000000000000, 0)
Decimal('9')
def decimal_ceil(x):
    int_x = int(x)
    if x - int_x == 0:
        return int_x
    return int_x + 1

Just use potency to make this. import math

def lo_ceil(num, potency=0): # Use 0 for multiples of 1, 1 for multiples of 10, 2 for 100 ...
      n = num / (10.0 ** potency)
      c = math.ceil(n)
      return c * (10.0 ** potency)

lo_ceil(8.0000001, 1) # return 10
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top