Significant figures in the decimal module

南笙酒味 提交于 2019-11-29 14:23:04

Changing the decimal working precision to 2 digits is not a good idea, unless you absolutely only are going to perform a single operation.

You should always perform calculations at higher precision than the level of significance, and only round the final result. If you perform a long sequence of calculations and round to the number of significant digits at each step, errors will accumulate. The decimal module doesn't know whether any particular operation is one in a long sequence, or the final result, so it assumes that it shouldn't round more than necessary. Ideally it would use infinite precision, but that is too expensive so the Python developers settled for 28 digits.

Once you've arrived at the final result, what you probably want is quantize:

>>> (Decimal('1.00') / Decimal('3.00')).quantize(Decimal("0.001"))
Decimal("0.333")

You have to keep track of significance manually. If you want automatic significance tracking, you should use interval arithmetic. There are some libraries available for Python, including pyinterval and mpmath (which supports arbitrary precision). It is also straightforward to implement interval arithmetic with the decimal library, since it supports directed rounding.

You may also want to read the Decimal Arithmetic FAQ: Is the decimal arithmetic ‘significance’ arithmetic?

Decimals won't throw away decimal places like that. If you really want to limit precision to 2 d.p. then try

decimal.getcontext().prec=2

EDIT: You can alternatively call quantize() every time you multiply or divide (addition and subtraction will preserve the 2 dps).

Just out of curiosity...is it necessary to use the decimal module? Why not floating point with a significant-figures rounding of numbers when you are ready to see them? Or are you trying to keep track of the significant figures of the computation (like when you have to do an error analysis of a result, calculating the computed error as a function of the uncertainties that went into the calculation)? If you want a rounding function that rounds from the left of the number instead of the right, try:

def lround(x,leadingDigits=0): 
    """Return x either as 'print' would show it (the default) 
    or rounded to the specified digit as counted from the leftmost 
    non-zero digit of the number, e.g. lround(0.00326,2) --> 0.0033
    """ 
    assert leadingDigits>=0 
    if leadingDigits==0: 
            return float(str(x)) #just give it back like 'print' would give it
    return float('%.*e' % (int(leadingDigits),x)) #give it back as rounded by the %e format  

The numbers will look right when you print them or convert them to strings, but if you are working at the prompt and don't explicitly print them they may look a bit strange:

>>> lround(1./3.,2),str(lround(1./3.,2)),str(lround(1./3.,4))
(0.33000000000000002, '0.33', '0.3333')

Decimal defaults to 28 places of precision.
The only way to limit the number of digits it returns is by altering the precision.

If I undertand Decimal correctly, the "precision" is the number of digits after the decimal point in decimal notation.

You seem to want something else: the number of significant digits. That is one more than the number of digits after the decimal point in scientific notation.

I would be interested in learning about a Python module that does significant-digits-aware floating point point computations.

What's wrong with floating point?

>>> "%8.2e"%  ( 1.0/3.0 )
'3.33e-01'

It was designed for scientific-style calculations with a limited number of significant digits.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!