zoukankan      html  css  js  c++  java
  • Python3标准库:decimal定点数和浮点数的数学运算

    1. decimal定点数和浮点数的数学运算

    decimal模块实现了定点和浮点算术运算,使用的是大多数人所熟悉的模型,而不是程序员熟悉的模式(即大多数计算机硬件实现的IEEE浮点数运算)。Decimal实例可以准确的表示如何数,对其上火其下取整,还可以限制有效数字的个数。

    1.1 Decimal

    小数值被表示为Decimal类的实例。构造函数取一个整数或字符串作为参数。在使用浮点数创建Decimal之前,可以先将浮点数转换为一个字符串,以使调用者能够显式的处理值的位数,因为如果使用硬件浮点数表示则可能无法准确的表述。或者,类方法from_float()可以把浮点数转换为精确的小数表示。

    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])
    )

    浮点值0.1并没有被表示为一个精确的二进制值,所以float的表示与Decimal值不同。在这个输出的最后一行,完整的字符串表示被截断为25个字符。

    Decimal还可以由元组创建,其中包含一个符号标志(0表示正,1表示负)、由数位组成的一个tuple以及一个整数指数。 

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

    基于元组的表示在创建时不太方便,不过它提供了一种可移植的方式,这样可以导出小数值而不损失精度。元组形式可以通过网络传输,或者在不支持精确小数值的数据库中存储,以后再转换回Decimal实例。

    1.2 格式化

    Decimal对应Python的字符串格式化协议,使用与其他数值类型一样的语法和选项。 

    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('
    Width 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('
    Zero padding:')
    print('{:05.1}'.format(d))
    print('{:05.2}'.format(d))
    print('{:05.3}'.format(d))

    格式字符串可以控制输出的宽度,精度(即有效数字个数),以及其填充值以占满宽度的方式。

    1.3 算术运算

    Decimal重载了简单的算术操作符,所以可以采用与内置数值类型相同的方式来处理Decimal实例。

    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操作符还接受整数参数,不过,在这些操作符使用浮点值之前必须把浮点值转换为Decimal实例。

    除了基本算术运算,Decimal还包括一些方法来查找以10为底的对数和自然对数。log10()和ln()返回的值都是Decimal实例,所以可以与其他值一样在公式中直接使用。

    1.4 特殊值

    除了期望的数字值,Decimal还可以表示很多特殊值,包括正负无穷大值、“不是一个数”(NaN)和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))

    与无穷大值相加会返回另一个无穷大值。与NaN比较相等性总会返回false,而比较不等性总会返回true。与NaN比较大小来确定排序顺序是未定义的,这会导致一个错误。

    1.5 上下文

    到目前为止,前面的所有例子使用的都是decimal模块的默认行为。还可以使用一个上下文(context)来覆盖某些设置,如保持的精度、如何完成取整、错误处理等。上下文可以应用于一个线程中的所有Decimal实例,或者在一个小代码区中本地应用。

    1.5.1 当前上下文

    要获取当前全局上下文,可以使用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))

    这个示例脚本显示了Context的公共属性。

    1.5.2 精度

    上下文的prec属性控制了作为算术运算结果创建的新值所要保持的精度。字面量值会按这个属性保持精度。 

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

    要改变精度,可以直接为这个属性赋一个1到decimal.MAX_PREC之间的新值。

    1.5.3 取整

    取整有多种选择,以保证值在所需的精度范围内。

    ROUND_CEILING:总是趋向无穷大向上取整。

    ROUND_DOWN:总是趋向0取整。

    ROUND_FLOOR:总是趋向负无穷大向下取整。

    ROUND_HALF_DOWN:如果最后一个有效数字大于或大于5则朝0反方向取整;负责,趋向0取整。

    ROUND_HALF_EVEN:类似于ROUND_HALF_DOWN,不过,如果最后一个有效数字为5,则会检查前一位。偶数值会导致结果向下取整,奇数值导致结果向上取整。

    ROUND_HALF_UP:类似于ROUND_HALF_DOWN,不过如果最后一位有效数字为5,则值会朝0的反方向取整。

    ROUND_UP:朝0的反方向取整。

    ROUND_05UP:如果最后一位是0或5,则朝0的反方向取整;否则向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()

    这个程序显示了使用不同算法将同一个值取整为不同精度的效果。

    1.5.4 本地上下文

    可以使用with语句对一个代码块应用上下文。

    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支持with使用的上下文管理器API,所以这个设置只在块内应用。

    1.5.5 各实例的上下文

    还可以用上下文构造Decimal实例,然后从这个上下文继承精度以及转换的取整参数。

    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)

    例如,这样一来,应用就可以选择与用户数据精度不同的常用值精度。

    1.5.6 线程

    “全局”上下文实例上是线程本地上下文,所以完全可以使用不同的值分别配置各个线程。

    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))

    这个例子使用指定的值来创建一个新的上下文,然后安装到每个线程中。

  • 相关阅读:
    关于程序出现 “因为应用程序正在发送一个输入同步呼叫,所以无法执行传出的呼叫”
    循环物理依赖
    重新生成执行计划
    SQL SERVER 2008 存储过程传表参数
    关于operator void* 操作符
    关于C++编译时内链接和外链接
    低级键盘钩子,在WIN7以上版本的问题
    关于SendMessage和PostMessage的理解的例子
    一个简单代码
    GET 和 POST 比较整理
  • 原文地址:https://www.cnblogs.com/liuhui0308/p/12386055.html
Copyright © 2011-2022 走看看