2

The best way I've found to produce a decimal.Decimal number with a specific number of significant figures is the one used to initialize the variable kluge below:

import decimal
THIS_IS_JUST_AN_EXAMPLE = 0.00001/3.0
with decimal.localcontext() as c:
   c.prec = 5
   kluge = decimal.Decimal(THIS_IS_JUST_AN_EXAMPLE) + decimal.Decimal(0)
   naive = decimal.Decimal(THIS_IS_JUST_AN_EXAMPLE)
   print repr(kluge)
   print repr(naive)

# Decimal('0.0000033333')
# Decimal('0.00000333333333333333374718233758915442166426146286539733409881591796875')

The first line of output shows the desired decimal.Decimal number, with 5 significant figures. The second line in the output shows what happens if one does not add the

+ decimal.Decimal(0)

to the RHS of the initialization; without it, the result has unwanted and unhelpful excess precision.

Even without the + decimal.Decimal(0) hack, the code strikes me as more verbose than it needs to be.

What's the correct approach to creating a Decimal with a desired number of significant figures?


This question is not a duplicate of Format Python Decimal object to a specified precision. Look at the output below.

import math
import decimal
base = math.pi
with decimal.localcontext() as c:
    c.prec = 13
    for exponent in [6, 4, 2, 0, -2, -4, -6]:
        scale = 10**exponent
        kluge = decimal.Decimal(base * scale) + decimal.Decimal(0)
        print repr(kluge)

# Decimal('3141592.653590')
# Decimal('31415.92653590')
# Decimal('314.1592653590')
# Decimal('3.141592653590')
# Decimal('0.03141592653590')
# Decimal('0.0003141592653590')
# Decimal('0.000003141592653590')

The items shown in the output above cannot be obtained using the method given in the answer to Format Python Decimal object to a specified precision, simply because this question has nothing to do with formatting output.

This question is about how to use a specific module (decimal) to create certain objects of a class defined by that module. More specifically, it is about looking for a way to use the decimal module to achieve the result shown without having to resort to adding decimal.Decimal(0).

Community
  • 1
  • 1
kjo
  • 33,683
  • 52
  • 148
  • 265

1 Answers1

3

You seem to be looking for Context.create_decimal:

Creates a new Decimal instance from num but using self as context. Unlike the Decimal constructor, the context precision, rounding method, flags, and traps are applied to the conversion.

>>> decimal.Context(prec=5).create_decimal(0.00001/3.0)
Decimal('0.0000033333')
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Thanks. I had originally accepted this answer, but subsequently I discovered inputs (not at all uncommon ones) for which this method fails to produce the right number of significant figures. (http://stackoverflow.com/questions/34211451/possible-bug-in-decimal) – kjo Dec 10 '15 at 21:47
  • @kjo: Oh hey, you're the same guy from the other question. I didn't notice. If you want trailing zeros, you can get them, but this starts to look more like an issue that should be handled in the display logic rather than the internal representation. Since you specifically emphasized that it's not a display issue, I interpreted this as a question about rounding, where trailing zeros wouldn't be important. What do you need trailing zeros for? – user2357112 Dec 10 '15 at 22:15
  • The trailing zeros follow from the definition of "significant figures". I'm beginning to realize, to my amazement, that Python is ignorant about the concept of "significant figures"... – kjo Dec 10 '15 at 22:23
  • @kjo: Sigfigs aren't usually the best way to do calculations on a computer. `decimal` will keep track of them to a limited degree, but you won't get anything like `1.20 + 0.02234 == 1.22` out of `decimal` like you would doing sigfigs by hand. I can give you a hacky thing with `quantize` to get you your trailing zeros, but if you want decimal to follow the sigfig rules, you'll just run into more problems. – user2357112 Dec 10 '15 at 22:47