Python 数字运算及格式化

数字运算及格式化


本篇幅主要针对浮点数的一些计算及格式化输出。

round()


Python 提供的内置函数 round(number[, ndigits]) 可用于简单的舍入运算。如下示例:

>>> round(1.23)
1
>>> round(1.23, 1)
1.2
>>> round(1.27, 1)
1.3
>>> round(-1.27, 1)
-1.3
>>> round(1.2346, 3)
1.235

round() 函数返回四舍五入到小数点后 ndigits 位精度的数字。如果 ndigits 默认为 None,则返回最接近的整数。

ndigits 参数可以是负数,在这种情况下,舍入运算会作用在十位、百位、千位等上面。例如:

>>> x = 1234567
>>> round(x, -1)
1234570
>>> round(x, -2)
1234600
>>> round(x, -3)
1235000

如果一个数字刚好在两个边界中间时,调用 round() 函数会返回离它最近的偶数。例如 round(0.5)round(-0.5) 返回的结果都是 0。如下示例:

>>> round(0.5)
0
>>> round(-0.5)
0

有时运用 round() 会得不到期望的值。例如 round(2.675, 2) 得到的是 2.67,却不是期望得到的 2.68。如下:

>>> round(2.675, 2)
2.67

这并非是 bug。Python 文档也给出了其中的注解,这是因为大多数十进制小数实际上都不能以浮点数精确地表示。

Note:The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float.

若需了解更多信息可以访问: Floating Point Arithmetic: Issues and Limitations。

格式化输出


数字舍入跟格式化不能混淆。若是简单输出指定宽度的数,可以考虑用 format(),不需要使用 round()。在格式化的时候指定精度就可以。例如:

>>> x = 1234.56789
>>> # 精确到小数点后两位
... format(x, '0.2f')
'1234.57'
>>> # 以 10 个字符右对齐,精确到小数点 1 位
... format(x, '>10.1f')
'    1234.6'
>>> # 左对齐
... format(x, '<10.1f')
'1234.6    '
>>> # 居中
... format(x, '^10.1f')
'  1234.6  '
>>> # 包含千分位分隔符
... format(x, ',')
'1,234.56789'
>>> format(x, '0,.1f')
'1,234.6'
>>>

同样可以使用指数记法,将 f 改成 e 或者 E(取决于指数输出的大小写形式)。示例如下:

>>> format(x, 'e')
'1.234568e+03'
>>> format(x, '0.2E')
'1.23E+03'

指定宽度和精度的一般形式为 '[<>^]?width[,]?(.digits)?',其中 widthdigits 为整数,? 代表可选部分。这种格式可以用在字符串的 format() 方法中。示例如下:

>>> 'The value is {:0,.2f}'.format(x)
'The value is 1,234.57'

精确运算


因为浮点数运算存在误差,不建议尝试使用舍入浮点值来“修正”表面上看起来正确的问题。示例如下:

>>> a = 2.1
>>> b = 4.2
>>> c = a + b
>>> c
6.300000000000001
>>> c = round(c, 2)  # 不建议这么做
>>> c
6.3

一般使用浮点数的情况下,不建议这么做。这些误差在一般情况下能够被容忍的。但是如果涉及到比如金融领域(不允许小误差存在)的情况下,建议考虑使用 decimal 模块。

decimal

由于浮点数不能精确的表示十进制,会出现上述简单数学运算出现误差的情况。

Python 提供的 decimal 模块,在损耗一定性能的前提下,能够解决这个问题。如下示例:

>>> from decimal import Decimal
>>> a = Decimal('4.2')
>>> b = Decimal('2.1')
>>> a + b
Decimal('6.3')
>>> print(a + b)
6.3
>>> (a + b) == Decimal('6.3')
True

虽然代码写起来会有些奇怪,但是 Decimal 对象能够像普通的浮点数支持所有的常用数学运算。在使用 print 的情况下,跟普通数字没有区别。

decimal 允许控制计算,包括精确位数跟舍入运算。可以通过创建上下文管理器进行设置更改,示例如下:

>>> from decimal import localcontext
>>> a = Decimal('1.3')
>>> b = Decimal('1.7')
>>> print(a/b)
0.7647058823529411764705882353
>>> with localcontext() as ctx:
...     ctx.prec = 3  # 精确位数
...     print(a/b)
...
0.765
>>> with localcontext() as ctx:
...     ctx.prec = 50  # 精确位数
...     print(a/b)
...
0.76470588235294117647058823529411764705882352941176

decimal 运用场景,更常见的是在金融领域(不能容忍小误差存在)。

但是 decimal,需要损耗性能进行精确的运算,在普通领域,甚至于科学领域的大多数运算,使用普通浮点数类型是普遍的做法。

除了真实世界很少要求要精确到普通浮点数能提供的 17 位精度的原因外,还有进行大量运算的时候,普通浮点数要快得多,这同样是需要衡量的地方。

但也不能完全忽略误差,误差出现,同样需要研究并理解误差产生的来源。

参考资料


来源

  1. David M. Beazley;Brian K. Jones.Python Cookbook, 3rd Edtioni.O'Reilly Media.2013.
  2. '2. Built-in Functions".docs.python.org.Retrieved 6 January 2020
  3. "15. Floating Point Arithmetic: Issues and Limitations".docs.python.org.Retrieved 7 January 2020
  4. "9.4. decimal — Decimal fixed point and floating point arithmetic".docs.python.org.Retrieved 11 January 2020

以上就是本篇的主要内容


欢迎关注『书所集录』公众号


你可能感兴趣的:(Python 数字运算及格式化)