Python 3 Standard Library: Mathematical operations for decimal fixed-point and floating-point numbers

1. Mathematical operations of decimal fixed-point and floating-point numbers

The decimal module implements fixed-point and floating-point arithmetic operations using a model that is familiar to most people, not to programmers (that is, IEEE floating-point arithmetic implemented by most computer hardware).Decimal instances can accurately represent how many numbers are, rounding them off, and limiting the number of valid numbers.

1.1 Decimal

The decimal value is represented as an instance of the Decimal class.The constructor takes an integer or string as a parameter.Before using floating-point numbers to create a Decimal, you can convert the floating-point number to a string so that the caller can explicitly handle the number of bits in the value, because a hardware floating-point representation may not be accurate.Alternatively, the class method from_float() can convert floating-point numbers to exact decimal representations.

import decimal

fmt = '{0:<25} {1:<25}'

print(fmt.format('Input', 'Output'))
print(fmt.format('-' * 25, '-' * 25))

# Integer
print(fmt.format(5, decimal.Decimal(5)))

# String
print(fmt.format('3.14', decimal.Decimal('3.14')))

# Float
f = 0.1
print(fmt.format(repr(f), decimal.Decimal(str(f))))
print('{:<0.23g} {:<25}'.format(
    f,
    str(decimal.Decimal.from_float(f))[:25])
)

Floating point value 0.1 is not represented as an exact binary value, so float is not represented as a Decimal value.In the last line of this output, the full string representation is truncated to 25 characters.

Decimal can also be created from tuples containing a symbol symbol symbol (0 for positive and 1 for negative), a tuple of digits, and an integer index.(

import decimal

# Tuple
t = (1, (1, 1), -2)
print('Input  :', t)
print('Decimal:', decimal.Decimal(t))

Tuple-based representations are not easy to create, but they provide a portable way to export decimal values without losing precision.Tuples can be transported over the network or stored in databases that do not support precise decimal values and then converted back to Decimal instances.

1.2 Formatting

Decimal corresponds to Python's string formatting protocol, using the same syntax and options as other numeric types.(

import decimal

d = decimal.Decimal(1.1)
print('Precision:')
print('{:.1}'.format(d))
print('{:.2}'.format(d))
print('{:.3}'.format(d))
print('{:.18}'.format(d))

print('\nWidth and precision combined:')
print('{:5.1f} {:5.1g}'.format(d, d))
print('{:5.2f} {:5.2g}'.format(d, d))
print('{:5.2f} {:5.2g}'.format(d, d))

print('\nZero padding:')
print('{:05.1}'.format(d))
print('{:05.2}'.format(d))
print('{:05.3}'.format(d))

The format string controls the width, precision (that is, the number of significant digits) of the output, and the way its fill values occupy the width.

1.3 Arithmetic operations

Decimal overloads simple arithmetic operators, so you can handle Decimal instances in the same way as built-in numeric types.

import decimal

a = decimal.Decimal('5.1')
b = decimal.Decimal('3.14')
c = 4
d = 3.14

print('a     =', repr(a))
print('b     =', repr(b))
print('c     =', repr(c))
print('d     =', repr(d))
print()

print('a + b =', a + b)
print('a - b =', a - b)
print('a * b =', a * b)
print('a / b =', a / b)
print()

print('a + c =', a + c)
print('a - c =', a - c)
print('a * c =', a * c)
print('a / c =', a / c)
print()

print('a + d =', end=' ')
try:
    print(a + d)
except TypeError as e:
    print(e)

Decimal operators also accept integer parameters, but floating-point values must be converted to Decimal instances before they can be used.

In addition to basic arithmetic operations, Decimal also includes methods for finding base-10 logarithms and natural logarithms.The values returned by log10() and ln() are Decimal instances, so they can be used directly in the formula like other values.

1.4 Special Value

In addition to the expected numeric values, Decimal can represent many special values, including positive and negative infinity, Not a Number (NaN), and 0.

import decimal

for value in ['Infinity', 'NaN', '0']:
    print(decimal.Decimal(value), decimal.Decimal('-' + value))
print()

# Math with infinity
print('Infinity + 1:', (decimal.Decimal('Infinity') + 1))
print('-Infinity + 1:', (decimal.Decimal('-Infinity') + 1))

# Print comparing NaN
print(decimal.Decimal('NaN') == decimal.Decimal('Infinity'))
print(decimal.Decimal('NaN') != decimal.Decimal(1))

Adding to an infinite value returns another infinite value.Comparing equality with NaN always returns false, while comparing inequality always returns true.Comparing the size with NaN to determine the sort order is undefined, which results in an error.

1.5 Context

So far, all previous examples have used the default behavior of the decimal module.You can also use a context to override certain settings, such as maintaining accuracy, how to complete rounding, error handling, and so on.Context can be applied to all Decimal instances in a thread or locally in a small code area.

1.5.1 Current Context

To get the current global context, you can use getcontext().

import decimal

context = decimal.getcontext()

print('Emax     =', context.Emax)
print('Emin     =', context.Emin)
print('capitals =', context.capitals)
print('prec     =', context.prec)
print('rounding =', context.rounding)
print('flags    =')
for f, v in context.flags.items():
    print('  {}: {}'.format(f, v))
print('traps    =')
for t, v in context.traps.items():
    print('  {}: {}'.format(t, v))

This sample script shows the common properties of the Context.

1.5.2 Precision

The prec property of the context controls the precision to be maintained for new values created as arithmetic results.Literal values maintain accuracy by this attribute.(

import decimal

d = decimal.Decimal('0.123456')

for i in range(1, 5):
    decimal.getcontext().prec = i
    print(i, ':', d, d * 1)

To change the precision, you can assign a new value directly between 1 and decimal.MAX_PREC for this attribute.

1.5.3 rounding

Rounding has several options to ensure that the values are within the required accuracy range.

ROUND_CEILING: Always rounds up towards infinity.

ROUND_DOWN: Always tends to zero rounding.

ROUND_FLOOR: Always rounds down toward negative infinity.

ROUND_HALF_DOWN: If the last valid number is greater than or greater than 5, it is rounded in the opposite direction of 0; responsible, it is rounded in the direction of 0.

ROUND_HALF_EVEN: Similar to ROUND_HALF_DOWN, however, if the last valid number is 5, the previous digit is checked.Even values cause the result to be rounded down, while odd values cause the result to be rounded up.

ROUND_HALF_UP: Similar to ROUND_HALF_DOWN, but if the last significant number is 5, the value is rounded in the opposite direction of 0.

ROUND_UP: Rounds in the opposite direction of 0.

ROUND_05UP: If the last bit is 0 or 5, it is rounded in the opposite direction of 0; otherwise, it is rounded to 0.

import decimal

context = decimal.getcontext()

ROUNDING_MODES = [
    'ROUND_CEILING',
    'ROUND_DOWN',
    'ROUND_FLOOR',
    'ROUND_HALF_DOWN',
    'ROUND_HALF_EVEN',
    'ROUND_HALF_UP',
    'ROUND_UP',
    'ROUND_05UP',
]

header_fmt = '{:10} ' + ' '.join(['{:^8}'] * 6)

print(header_fmt.format(
    ' ',
    '1/8 (1)', '-1/8 (1)',
    '1/8 (2)', '-1/8 (2)',
    '1/8 (3)', '-1/8 (3)',
))
for rounding_mode in ROUNDING_MODES:
    print('{0:10}'.format(rounding_mode.partition('_')[-1]),
          end=' ')
    for precision in [1, 2, 3]:
        context.prec = precision
        context.rounding = getattr(decimal, rounding_mode)
        value = decimal.Decimal(1) / decimal.Decimal(8)
        print('{0:^8}'.format(value), end=' ')
        value = decimal.Decimal(-1) / decimal.Decimal(8)
        print('{0:^8}'.format(value), end=' ')
    print()

This program shows the effect of using different algorithms to round the same value into different precision.

1.5.4 Local Context

You can use the with statement to apply a context to a block of code.

import decimal

with decimal.localcontext() as c:
    c.prec = 2
    print('Local precision:', c.prec)
    print('3.14 / 3 =', (decimal.Decimal('3.14') / 3))

print()
print('Default precision:', decimal.getcontext().prec)
print('3.14 / 3 =', (decimal.Decimal('3.14') / 3))

Context supports the context manager API used by with, so this setting is only applied within a block.

1.5.5 Context for each instance

You can also construct a Decimal instance with a context from which precision and rounding parameters of the transformation can be inherited.

import decimal

# Set up a context with limited precision
c = decimal.getcontext().copy()
c.prec = 3

# Create our constant
pi = c.create_decimal('3.1415')

# The constant value is rounded off
print('PI    :', pi)

# The result of using the constant uses the global context
print('RESULT:', decimal.Decimal('2.01') * pi)

For example, this allows applications to select common value accuracy that differs from user data accuracy.

1.5.6 Threads

The Global context instance is the thread local context, so you can configure threads individually using different values.

import decimal
import threading
from queue import PriorityQueue


class Multiplier(threading.Thread):
    def __init__(self, a, b, prec, q):
        self.a = a
        self.b = b
        self.prec = prec
        self.q = q
        threading.Thread.__init__(self)

    def run(self):
        c = decimal.getcontext().copy()
        c.prec = self.prec
        decimal.setcontext(c)
        self.q.put((self.prec, a * b))


a = decimal.Decimal('3.14')
b = decimal.Decimal('1.234')
# A PriorityQueue will return values sorted by precision,
# no matter what order the threads finish.
q = PriorityQueue()
threads = [Multiplier(a, b, i, q) for i in range(1, 6)]
for t in threads:
    t.start()

for t in threads:
    t.join()

for i in range(5):
    prec, value = q.get()
    print('{}  {}'.format(prec, value))

This example uses the specified value to create a new context and install it on each thread.

Keywords: Python Attribute network

Added by quadlo on Thu, 05 Mar 2020 04:00:05 +0200