写在前面
这是一篇通过例子学习Python标准库math的教程。math库提供了许多便捷的函数,能够计算常规数学运算、三角函数、双曲函数和部分特殊函数。
本文翻译自 Doug Hellmann 的 PyMOTW-3项目的math模块。原文链接。
本文使用cc-by-nc-sa 4.0协议共享。
math — 数学函数
目的:为特殊数学运算提供函数。
math
模块实现了许多IEEE使用浮点数进行复杂数学运算的函数,包括对数和三角函数运算,这些函数通常可以在本地C库找到。
特殊常量
许多数学运算基于特殊常量。math
提供的值有,
nan
(不是数字),和
# math_constants.py
import math
print(' π: {:.30f}'.format(math.pi))
print(' e: {:.30f}'.format(math.e))
print('nan: {:.30f}'.format(math.nan))
print('inf: {:.30f}'.format(math.inf))
$ python3 math_constants.py
π: 3.141592653589793115997963468544
e: 2.718281828459045090795598298428
nan: nan
inf: inf
测试异常值
浮点数计算会产生两类异常值。第一类是inf
(
# math_isinf.py
import math
print('{:^3} {:6} {:6} {:6}'.format(
'e', 'x', 'x**2', 'isinf'))
print('{:-^3} {:-^6} {:-^6} {:-^6}'.format(
'', '', '', ''))
for e in range(0, 201, 20):
x = 10.0 ** e
y = x * x
print('{:3d} {:<6g} {:<6g} {!s:6}'.format(
e, x, y, math.isinf(y),
))
当此例的指数增加到足够大时,x的平方不再包含于双精度值,并将该值记录为无穷。
$ python3 math_isinf.py
e x x**2 isinf
--- ------ ------ ------
0 1 1 False
20 1e+20 1e+40 False
40 1e+40 1e+80 False
60 1e+60 1e+120 False
80 1e+80 1e+160 False
100 1e+100 1e+200 False
120 1e+120 1e+240 False
140 1e+140 1e+280 False
160 1e+160 inf True
180 1e+180 inf True
200 1e+200 inf True
然而,并不是所有的浮点溢出都会导致inf
值。特别地,使用浮点数计算指数会导致OverflowError,而不是保留inf
结果。
# math_overflow.py
x = 10.0 ** 200
print('x =', x)
print('x*x =', x * x)
print('x**2 =', end=' ')
try:
print(x ** 2)
except OverflowError as err:
print(err)
这种差别是由C Python解释器使用的库的实现差异引起的。
$ python3 math_overflow.py
x = 1e+200
x*x = inf
x**2 = (34, 'Result too large')
使用无穷值的除法是未定义的。一个数除以无穷的结果是 nan
(不是数字)。
# math_isnan.py
import math
x = (10.0 ** 200) * (10.0 ** 200)
y = x / x
print('x =', x)
print('isnan(x) =', math.isnan(x))
print('y = x / x =', x / x)
print('y == nan =', y == float('nan'))
print('isnan(y) =', math.isnan(y))
nan
不与任何值相等,包括它本身,所以需用 isnan()
检查 nan
。
$ python3 math_isnan.py
x = inf
isnan(x) = False
y = x / x = nan
y == nan = False
isnan(y) = True
使用isfinite()
检查常规数与特殊值inf
或nan
。
# math_isfinite.py
import math
for f in [0.0, 1.0, math.pi, math.e, math.inf, math.nan]:
print('{:5.2f} {!s}'.format(f, math.isfinite(f)))
对于特殊值,isfinite()
返回 false,否则返回 true。
$ python3 math_isfinite.py
0.00 True
1.00 True
3.14 True
2.72 True
inf False
nan False
比较
比较浮点数会容易出错,这是因为,计算的每个步骤都可能由于数值表示而引入误差。isclose()
函数使用稳健的算法来最小化这些错误,并提供相对和绝对比较的方法。所用的公式等价于
abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
默认情况下,isclose()
使用公差设置为 1e-09
的相对比较,这意味着两值之差必须小于等于 1e-09
乘以a和b中较大的绝对值。将关键参数rel_tol
传给isclose()
可改变公差。在此例,这些值必须相差10%以内。
# math_isclose.py
import math
INPUTS = [
(1000, 900, 0.1),
(100, 90, 0.1),
(10, 9, 0.1),
(1, 0.9, 0.1),
(0.1, 0.09, 0.1),
]
print('{:^8} {:^8} {:^8} {:^8} {:^8} {:^8}'.format(
'a', 'b', 'rel_tol', 'abs(a-b)', 'tolerance', 'close')
)
print('{:-^8} {:-^8} {:-^8} {:-^8} {:-^8} {:-^8}'.format(
'-', '-', '-', '-', '-', '-'),
)
fmt = '{:8.2f} {:8.2f} {:8.2f} {:8.2f} {:8.2f} {!s:>8}'
for a, b, rel_tol in INPUTS:
close = math.isclose(a, b, rel_tol=rel_tol)
tolerance = rel_tol * max(abs(a), abs(b))
abs_diff = abs(a - b)
print(fmt.format(a, b, rel_tol, abs_diff, tolerance, close))
因为误差设置为0.1,0.1和0.09的比较返回Flase
。
$ python3 math_isclose.py
a b rel_tol abs(a-b) tolerance close
-------- -------- -------- -------- -------- --------
1000.00 900.00 0.10 100.00 100.00 True
100.00 90.00 0.10 10.00 10.00 True
10.00 9.00 0.10 1.00 1.00 True
1.00 0.90 0.10 0.10 0.10 True
0.10 0.09 0.10 0.01 0.01 False
要使用固定或“绝对”的公差,请传递abs_tol
而不是rel_tol
。
# math_isclose_abs_tol.py
import math
INPUTS = [
(1.0, 1.0 + 1e-07, 1e-08),
(1.0, 1.0 + 1e-08, 1e-08),
(1.0, 1.0 + 1e-09, 1e-08),
]
print('{:^8} {:^11} {:^8} {:^10} {:^8}'.format(
'a', 'b', 'abs_tol', 'abs(a-b)', 'close')
)
print('{:-^8} {:-^11} {:-^8} {:-^10} {:-^8}'.format(
'-', '-', '-', '-',