【译】Python 标准库教程——math 模块

这是一篇通过例子学习 Python 标准库 math 的教程。math 库提供了许多便捷的函数,能够计算常规数学运算、三角函数、双曲函数和部分特殊函数。

本文翻译自 Doug Hellmann 的 PyMOTW-3 项目的 math 模块。原文链接。本文使用cc-by-nc-sa 4.0协议共享。

math — 数学函数

目的: 为特殊数学运算提供函数。

math 模块实现了许多 IEEE 使用浮点数进行复杂数学运算的函数,包括对数和三角函数运算,这些函数通常可以在本地 C 库找到。

特殊常量

许多数学运算基于特殊常量。math提供的值有, $\pi$(pi), e, nan(不是数字),和$\infin$。

1
2
3
4
5
6
7
8
9
# 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))

$\pi$和 e 的精度仅受平台浮点C语言库限制。

1
2
3
4
5
6
$ python3 math_constants.py

π: 3.141592653589793115997963468544
e: 2.718281828459045090795598298428
nan: nan
inf: inf

测试异常值

浮点数计算会产生两类异常值。第一类是inf( $\infin$),当用于保存浮点数的双精度值从绝对值较大的数溢出时出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 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 的平方不再包含于双精度值,并将该值记录为无穷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ 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结果。

1
2
3
4
5
6
7
8
9
10
11
# 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 解释器使用的库的实现差异引起的。

1
2
3
4
5
$ python3 math_overflow.py

x = 1e+200
x*x = inf
x**2 = (34, 'Result too large')

使用无穷值的除法是未定义的。一个数除以无穷的结果是 nan(不是数字)。

1
2
3
4
5
6
7
8
9
10
11
12
# 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

1
2
3
4
5
6
7
$ python3 math_isnan.py

x = inf
isnan(x) = False
y = x / x = nan
y == nan = False
isnan(y) = True

使用isfinite()检查常规数与特殊值infnan

1
2
3
4
5
6
# 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。

1
2
3
4
5
6
7
8
$ python3 math_isfinite.py

0.00 True
1.00 True
3.14 True
2.72 True
inf False
nan False

比较

比较浮点数会容易出错,这是因为,计算的每个步骤都可能由于数值表示而引入误差。isclose()函数使用稳健的算法来最小化这些错误,并提供相对和绝对比较的方法。所用的公式等价于

1
abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

默认情况下,isclose()使用公差设置为 1e-09的相对比较,这意味着两值之差必须小于等于 1e-09乘以 a 和 b 中较大的绝对值。将关键参数rel_tol传给isclose()可改变公差。在此例,这些值必须相差 10%以内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 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

1
2
3
4
5
6
7
$ 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 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(
'-', '-', '-', '-', '-'),
)

for a, b, abs_tol in INPUTS:
close = math.isclose(a, b, abs_tol=abs_tol)
abs_diff = abs(a - b)
print('{:8.2f} {:11} {:8} {:0.9f} {!s:>8}'.format(
a, b, abs_tol, abs_diff, close))

对于绝对公差,输入值之间的差必须小于给定的公差。

1
2
3
4
5
$ python3 math_isclose_abs_tol.py
## a b abs_tol abs(a-b) close ----------- -------- ---------- --------
1.00 1.0000001 1e-08 0.000000100 False
1.00 1.00000001 1e-08 0.000000010 True
1.00 1.000000001 1e-08 0.000000001 True

naninf是特例。

1
2
3
4
5
6
7
8
# math_isclose_inf.py

import math

print('nan, nan:', math.isclose(math.nan, math.nan))
print('nan, 1.0:', math.isclose(math.nan, 1.0))
print('inf, inf:', math.isclose(math.inf, math.inf))
print('inf, 1.0:', math.isclose(math.inf, 1.0))

nan永远不接近另一个值,包括它本身。 inf仅仅接近自身。

1
2
3
4
5
6
$ python3 math_isclose_inf.py

nan, nan: False
nan, 1.0: False
inf, inf: True
inf, 1.0: False

浮点数到整数的转换

math 模块提供了三个用来转换浮点数为整数的函数。每个函数都采用不同的方法,并将在不同的情况下很有用。

最简单的是trunc(),它会截断小数点后的数字,仅保留构成值的整数部分的有效数字。floor()将输入值转换为最大的在前整数,ceil()(天花板) 返回在输入值之后顺序产生最大的整数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# math_integers.py

import math

HEADINGS = ('i', 'int', 'trunk', 'floor', 'ceil')
print('{:^5} {:^5} {:^5} {:^5} {:^5}'.format(*HEADINGS))
print('{:-^5} {:-^5} {:-^5} {:-^5} {:-^5}'.format(
'', '', '', '', '',
))

fmt = '{:5.1f} {:5.1f} {:5.1f} {:5.1f} {:5.1f}'

TEST_VALUES = [
-1.5,
-0.8,
-0.5,
-0.2,
0,
0.2,
0.5,
0.8,
1,
]

for i in TEST_VALUES:
print(fmt.format(
i,
int(i),
math.trunc(i),
math.floor(i),
math.ceil(i),
))

trunc()等价于直接转换为int

1
2
3
4
5
6
7
8
9
10
11
$ python3 math_integers.py
## i int trunk floor ceil ----- ----- ----- -----
-1.5 -1.0 -1.0 -2.0 -1.0
-0.8 0.0 0.0 -1.0 0.0
-0.5 0.0 0.0 -1.0 0.0
-0.2 0.0 0.0 -1.0 0.0
0.0 0.0 0.0 0.0 0.0
0.2 0.0 0.0 0.0 1.0
0.5 0.0 0.0 0.0 1.0
0.8 0.0 0.0 0.0 1.0
1.0 1.0 1.0 1.0 1.0

modf()接受单个浮点数,并返回一个包含输入值的小数和整数部分的元组。

1
2
3
4
5
6
# math_modf.py

import math

for i in range(6):
print('{}/2 = {}'.format(i, math.modf(i / 2.0)))

返回值中的两数均为浮点数。

1
2
3
4
5
6
7
8
$ python3 math_modf.py

0/2 = (0.0, 0.0)
1/2 = (0.5, 0.0)
2/2 = (0.0, 1.0)
3/2 = (0.5, 1.0)
4/2 = (0.0, 2.0)
5/2 = (0.5, 2.0)

frexp()返回浮点数的尾数和指数,可用于创建该值更可移植的表示形式。

1
2
3
4
5
6
7
8
9
math_frexp.py
import math

print('{:^7} {:^7} {:^7}'.format('x', 'm', 'e'))
print('{:-^7} {:-^7} {:-^7}'.format('', '', ''))

for x in [0.1, 0.5, 4.0]:
m, e = math.frexp(x)
print('{:7.2f} {:7.2f} {:7d}'.format(x, m, e))

frexp()使用公式x = m * 2 ** e,并返回值me

1
2
3
4
5
$ python3 math_frexp.py
## x m e ------- -------
0.10 0.80 -3
0.50 0.50 0
4.00 0.50 3

ldexp()frexp()的反函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# math_ldexp.py

import math

print('{:^7} {:^7} {:^7}'.format('m', 'e', 'x'))
print('{:-^7} {:-^7} {:-^7}'.format('', '', ''))

INPUTS = [
(0.8, -3),
(0.5, 0),
(0.5, 3),
]

for m, e in INPUTS:
x = math.ldexp(m, e)
print('{:7.2f} {:7d} {:7.2f}'.format(m, e, x))

正负号

数字的绝对值是不带符号的值。 使用fabs()计算浮点数的绝对值。

1
2
3
4
5
6
7
8
# math_fabs.py

import math

print(math.fabs(-1.1))
print(math.fabs(-0.0))
print(math.fabs(0.0))
print(math.fabs(1.1))

实际上,浮点数的绝对值表示为正值。

1
2
3
4
5
6
$ python3 math_fabs.py

1.1
0.0
0.0
1.1

为确定值的符号,可以赋予一组值相同的符号或比较两个值,使用copysign()设置已知有效值的符号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# math_copysign.py

import math

HEADINGS = ('f', 's', '< 0', '> 0', '= 0')
print('{:^5} {:^5} {:^5} {:^5} {:^5}'.format(*HEADINGS))
print('{:-^5} {:-^5} {:-^5} {:-^5} {:-^5}'.format(
'', '', '', '', '',
))

VALUES = [
-1.0,
0.0,
1.0,
float('-inf'),
float('inf'),
float('-nan'),
float('nan'),
]

for f in VALUES:
s = int(math.copysign(1, f))
print('{:5.1f} {:5d} {!s:5} {!s:5} {!s:5}'.format(
f, s, f < 0, f > 0, f == 0,
))

copysign()这样的额外函数是必需的,因为把nan-nan直接与其他值比较是不可行的。

1
2
3
4
5
6
7
8
9
$ python3 math_copysign.py
## f s < 0 > 0 = 0 ----- ----- ----- -----
-1.0 -1 True False False
0.0 1 False False True
1.0 1 False True False
-inf -1 True False False
inf 1 False True False
nan -1 False False False
nan 1 False False False

常用计算

在二进制浮点内存中表示准确值具有挑战性。一些值不能被准确表示,并且通过重复计算操作某个值的频率越高,出现表示错误的可能性越高。math模块提供了计算浮点数序列之和的函数,该函数使用有效的算法最小化这些错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# math_fsum.py

import math

values = [0.1] * 10

print('Input values:', values)

print('sum() : {:.20f}'.format(sum(values)))

s = 0.0
for i in values:
s += i
print('for-loop : {:.20f}'.format(s))

print('math.fsum() : {:.20f}'.format(math.fsum(values)))

给定十个值的序列,每个值都是 0.1,序列和的预期值为 1.0。然而,由于 0.1 不能被精确地表示为浮点数,误差将被引入到求和中,除非使用fsum()进行计算。

1
2
3
4
5
6
$ python3 math_fsum.py

Input values: [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
sum() : 0.99999999999999988898
for-loop : 0.99999999999999988898
math.fsum() : 1.00000000000000000000

factorial()通常用来计算对象序列的排列数和组合数。正整数 n 的阶乘,表示为 n! ,其定义为递归地 (n - 1) !* n,以 0!= = 1 结束。

1
2
3
4
5
6
7
8
9
# math_factorial.py

import math

for i in [0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.1]:
try:
print('{:2.0f} {:6.0f}'.format(i, math.factorial(i)))
except ValueError as err:
print('Error computing factorial({}): {}'.format(i, err))

factorial()仅适用于整数,但会接受浮点参数,只要它们可以转换为整数而不会丢失值。

1
2
3
4
5
6
7
8
9
$ python3 math_factorial.py

0 1
1 1
2 2
3 6
4 24
5 120
Error computing factorial(6.1): factorial() only accepts integral values

gamma()factorial()相似,除了它处理的是实数且值向下移动了一位 ( gammar()等价于 (n-1)! )。

1
2
3
4
5
6
7
8
9
# math_gamma.py

import math

for i in [0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6]:
try:
print('{:2.1f} {:6.2f}'.format(i, math.gamma(i)))
except ValueError as err:
print('Error computing gamma({}): {}'.format(i, err))

由于 0 会导致初始值为负,故不允许这样做。

1
2
3
4
5
6
7
8
9
$ python3 math_gamma.py

Error computing gamma(0): math domain error
1.1 0.95
2.2 1.10
3.3 2.68
4.4 10.14
5.5 52.34
6.6 344.70

lgamma()返回输入值的 gamma 绝对值的自然对数。

1
2
3
4
5
6
7
8
9
10
11
12
13
# math_lgamma.py

import math

for i in [0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6]:
try:
print('{:2.1f} {:.20f} {:.20f}'.format(
i,
math.lgamma(i),
math.log(math.gamma(i)),
))
except ValueError as err:
print('Error computing lgamma({}): {}'.format(i, err))

使用lgamma()比使用gamma()的结果单独计算对数保留更高的精度。

1
2
3
4
5
6
7
8
9
$ python3 math_lgamma.py

Error computing lgamma(0): math domain error
1.1 -0.04987244125984036103 -0.04987244125983997245
2.2 0.09694746679063825923 0.09694746679063866168
3.3 0.98709857789473387513 0.98709857789473409717
4.4 2.31610349142485727469 2.31610349142485727469
5.5 3.95781396761871651080 3.95781396761871606671
6.6 5.84268005527463252236 5.84268005527463252236

取模运算符 (%) 用来计算除法表达式的余数 (i.e., 5 % 2 = 1) 。内置在语言中的算符可以很好的处理整数,但与许多其他浮点运算符一样,间接计算会导致数据丢失的代表性问题。fmod()为浮点数提供了更精确的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# math_fmod.py

import math

print('{:^4} {:^4} {:^5} {:^5}'.format(
'x', 'y', '%', 'fmod'))
print('{:-^4} {:-^4} {:-^5} {:-^5}'.format(
'-', '-', '-', '-'))

INPUTS = [
(5, 2),
(5, -2),
(-5, 2),
]

for x, y in INPUTS:
print('{:4.1f} {:4.1f} {:5.2f} {:5.2f}'.format(
x,
y,
x % y,
math.fmod(x, y),
))

一个更常见的潜在混淆源是,fmod()用于计算模数的算法与算符使用的算法不同,因此结果的符号不同。

1
2
3
4
5
$ python3 math_fmod.py
## x y % fmod ---- ----- -----
5.0 2.0 1.00 1.00
5.0 -2.0 -1.00 1.00
-5.0 2.0 1.00 -1.00

使用gcd()寻找能被两数整除的最大整数,即最大公约数。

1
2
3
4
5
6
7
8
9
# math_gcd.py

import math

print(math.gcd(10, 8))
print(math.gcd(10, 0))
print(math.gcd(50, 225))
print(math.gcd(11, 9))
print(math.gcd(0, 0))

如果两个值都为 0,则结果为 0。

1
2
3
4
5
6
7
$ python3 math_gcd.py

2
10
25
1
0

指数和对数

指数增长曲线出现在经济学,物理学和其他科学中。 Python 具有内置的幂运算符(“ **”),但当需要可调用函数作为另一个函数的参数时,pow()可能会很有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# math_pow.py

import math

INPUTS = [
# Typical uses
(2, 3),
(2.1, 3.2),

# Always 1
(1.0, 5),
(2.0, 0),

# Not-a-number
(2, float('nan')),

# Roots
(9.0, 0.5),
(27.0, 1.0 / 3),
]

for x, y in INPUTS:
print('{:5.1f} ** {:5.3f} = {:6.3f}'.format(
x, y, math.pow(x, y)))

将 1 增加到任意幂总是返回 1.0,将任意值增加到 0 次方也是如此。对非数字值nan的大多数操作都返回nan。 如果指数小于 1,则pow()计算开方。

1
2
3
4
5
6
7
8
9
$ python3 math_pow.py

2.0 ** 3.000 = 8.000
2.1 ** 3.200 = 10.742
1.0 ** 5.000 = 1.000
2.0 ** 0.000 = 1.000
2.0 ** nan = nan
9.0 ** 0.500 = 3.000
27.0 ** 0.333 = 3.000

由于平方根(1/2 的指数)被频繁使用,因此有一个可以单独计算的函数。

1
2
3
4
5
6
7
8
9
10
# math_sqrt.py

import math

print(math.sqrt(9.0))
print(math.sqrt(3))
try:
print(math.sqrt(-1))
except ValueError as err:
print('Cannot compute sqrt(-1):', err)

计算负数的平方根需要用到复数,这不是math能处理的。任何计算负数平方根的尝试都会导致 ValueError。

1
2
3
4
5
$ python3 math_sqrt.py

3.0
1.7320508075688772
Cannot compute sqrt(-1): math domain error

对数函数在当 x = b ** y 时找到 y 。默认情况下,log()计算自然对数 (以 e 为底数)。如果提供第二个参数,则以该值为底数。

1
2
3
4
5
6
7
# math_log.py

import math

print(math.log(8))
print(math.log(8, 2))
print(math.log(0.5, 2))

当 x < 1 时的对数结果为负。

1
2
3
4
5
$ python3 math_log.py

2.0794415416798357
3.0
-1.0

给定浮点数表示法和舍入误差,log(x,b)产生的计算值的准确性有限,尤其是对于某些基数而言。 log10()使用比log()更精确的算法来计算log(x,10)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# math_log10.py

import math

print('{:2} {:^12} {:^10} {:^20} {:8}'.format(
'i', 'x', 'accurate', 'inaccurate', 'mismatch',
))
print('{:-^2} {:-^12} {:-^10} {:-^20} {:-^8}'.format(
'', '', '', '', '',
))

for i in range(0, 10):
x = math.pow(10, i)
accurate = math.log10(x)
inaccurate = math.log(x, 10)
match = '' if int(inaccurate) == i else '*'
print('{:2d} {:12.1f} {:10.8f} {:20.18f} {:^5}'.format(
i, x, accurate, inaccurate, match,
))

输出结尾中带有 * 标记的显示了不精确的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ python3 math_log10.py

i x accurate inaccurate mismatch
-- ------------ ---------- -------------------- --------
0 1.0 0.00000000 0.000000000000000000
1 10.0 1.00000000 1.000000000000000000
2 100.0 2.00000000 2.000000000000000000
3 1000.0 3.00000000 2.999999999999999556 *
4 10000.0 4.00000000 4.000000000000000000
5 100000.0 5.00000000 5.000000000000000000
6 1000000.0 6.00000000 5.999999999999999112 *
7 10000000.0 7.00000000 7.000000000000000000
8 100000000.0 8.00000000 8.000000000000000000
9 1000000000.0 9.00000000 8.999999999999998224 *

log10()相似,log2()计算等价于math.log(x, 2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# math_log2.py
import math

print('{:>2} {:^5} {:^5}'.format(
'i', 'x', 'log2',
))
print('{:-^2} {:-^5} {:-^5}'.format(
'', '', '',
))

for i in range(0, 10):
x = math.pow(2, i)
result = math.log2(x)
print('{:2d} {:5.1f} {:5.1f}'.format(
i, x, result,
))

根据底层平台的不同,内置的和专用的函数通过使用以 2 为底的专用算法,可提供更好的性能和准确性,这是更通用的函数所没有的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ python3 math_log2.py

i x log2
-- ----- -----
0 1.0 0.0
1 2.0 1.0
2 4.0 2.0
3 8.0 3.0
4 16.0 4.0
5 32.0 5.0
6 64.0 6.0
7 128.0 7.0
8 256.0 8.0
9 512.0 9.0

log1p()计算牛顿 - 墨卡托级数 (1+x 的自然对数)。

1
2
3
4
5
6
7
8
9
# math_log1p.py

import math

x = 0.0000000000000000000000001
print('x :', x)
print('1 + x :', 1 + x)
print('log(1+x):', math.log(1 + x))
print('log1p(x):', math.log1p(x))

log1p()对于非常接近于零的 x 值更准确,因为它使用了一种补偿初始加法的四舍五入误差的算法。

1
2
3
4
5
6
$ python3 math_log1p.py

x : 1e-25
1 + x : 1.0
log(1+x): 0.0
log1p(x): 1e-25

exp()计算指数函数值 (e**x)。

1
2
3
4
5
6
7
8
9
# math_exp.py
import math

x = 2

fmt = '{:.20f}'
print(fmt.format(math.e ** 2))
print(fmt.format(math.pow(math.e, 2)))
print(fmt.format(math.exp(2)))

与其他特殊函数一样,它使用的算法所产生的结果比通用等效的math.pow(math.e,x)更为精确。

1
2
3
4
5
$ python3 math_exp.py

7.38905609893064951876
7.38905609893064951876
7.38905609893065040694

expm1()log1p()的反函数,用于计算e ** x - 1

1
2
3
4
5
6
7
8
9
# math_expm1.py

import math

x = 0.0000000000000000000000001

print(x)
print(math.exp(x) - 1)
print(math.expm1(x))

当分别执行减法时,x 的小值会丢失精度,就像使用log1p()

1
2
3
4
5
$ python3 math_expm1.py

1e-25
0.0
1e-25

角度

虽然角度制在日常生活中更常用,但在科学和数学中弧度制是衡量角度的标准单位。1 弧度是由两条线在圆心相交而形成的角度,两条线的末端在圆周上相距一个半径。

圆的周长记为2πr,因此弧度和之间存在一种联系,该值在三角函数计算中经常出现。这种联系使得弧度被应用在三角学和微积分中,因为使用它们可产生更紧凑的公式。

要将度转换为弧度,请使用radians()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# math_radians.py

import math

print('{:^7} {:^7} {:^7}'.format(
'Degrees', 'Radians', 'Expected'))
print('{:-^7} {:-^7} {:-^7}'.format(
'', '', ''))

INPUTS = [
(0, 0),
(30, math.pi / 6),
(45, math.pi / 4),
(60, math.pi / 3),
(90, math.pi / 2),
(180, math.pi),
(270, 3 / 2.0 * math.pi),
(360, 2 * math.pi),
]

for deg, expected in INPUTS:
print('{:7d} {:7.2f} {:7.2f}'.format(
deg,
math.radians(deg),
expected,
))

转换公式为rad = deg *π/ 180

1
2
3
4
5
6
7
8
9
10
$ python3 math_radians.py
## Degrees Radians Expected ------- -------
0 0.00 0.00
30 0.52 0.52
45 0.79 0.79
60 1.05 1.05
90 1.57 1.57
180 3.14 3.14
270 4.71 4.71
360 6.28 6.28

要将弧度转换为度,请使用degree()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# math_degrees.py

import math

INPUTS = [
(0, 0),
(math.pi / 6, 30),
(math.pi / 4, 45),
(math.pi / 3, 60),
(math.pi / 2, 90),
(math.pi, 180),
(3 * math.pi / 2, 270),
(2 * math.pi, 360),
]

print('{:^8} {:^8} {:^8}'.format(
'Radians', 'Degrees', 'Expected'))
print('{:-^8} {:-^8} {:-^8}'.format('', '', ''))
for rad, expected in INPUTS:
print('{:8.2f} {:8.2f} {:8.2f}'.format(
rad,
math.degrees(rad),
expected,
))

转换公式为deg = rad * 180 / π.

1
2
3
4
5
6
7
8
9
10
$ python3 math_degrees.py
## Radians Degrees Expected -------- --------
0.00 0.00 0.00
0.52 30.00 30.00
0.79 45.00 45.00
1.05 60.00 60.00
1.57 90.00 90.00
3.14 180.00 180.00
4.71 270.00 270.00
6.28 360.00 360.00

三角学

三角函数将三角形中的角度与其边长相关联。它们出现在周期性的公式中,如谐波、圆周运动,或处理角度时。标准库中的所有三角函数都采用以弧度表示的角度。给定直角三角形中的某个角度,正弦是与该角度相对的边与斜边的比值(sin A = 相对 / 斜边)。余弦是相邻边与斜边的长度之比(cos A = 相邻 / 斜边)。正切是对边和邻边之比 (tan A = 对边 / 邻边)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# math_trig.py

import math

print('{:^7} {:^7} {:^7} {:^7} {:^7}'.format(
'Degrees', 'Radians', 'Sine', 'Cosine', 'Tangent'))
print('{:-^7} {:-^7} {:-^7} {:-^7} {:-^7}'.format(
'-', '-', '-', '-', '-'))

fmt = '{:7.2f} {:7.2f} {:7.2f} {:7.2f} {:7.2f}'

for deg in range(0, 361, 30):
rad = math.radians(deg)
if deg in (90, 270):
t = float('inf')
else:
t = math.tan(rad)
print(fmt.format(deg, rad, math.sin(rad), math.cos(rad), t))

tan也可以被定义为角的正弦与其余弦之比,并且由于π/ 2 和 3π/ 2 弧度的余弦为 0,所以tan是无限大的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ python3 math_trig.py
## Degrees Radians Sine Cosine Tangent ------- ------- ------- -------
0.00 0.00 0.00 1.00 0.00
30.00 0.52 0.50 0.87 0.58
60.00 1.05 0.87 0.50 1.73
90.00 1.57 1.00 0.00 inf
120.00 2.09 0.87 -0.50 -1.73
150.00 2.62 0.50 -0.87 -0.58
180.00 3.14 0.00 -1.00 -0.00
210.00 3.67 -0.50 -0.87 0.58
240.00 4.19 -0.87 -0.50 1.73
270.00 4.71 -1.00 -0.00 inf
300.00 5.24 -0.87 0.50 -1.73
330.00 5.76 -0.50 0.87 -0.58
360.00 6.28 -0.00 1.00 -0.00

给定点(x,y),点[(0,0), (x,0), (x,y)]构成的三角形的斜边长为(x ** 2 + y ** 2 )** 1/2,可以用hypot()计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# math_hypot.py

import math

print('{:^7} {:^7} {:^10}'.format('X', 'Y', 'Hypotenuse'))
print('{:-^7} {:-^7} {:-^10}'.format('', '', ''))

POINTS = [
# simple points
(1, 1),
(-1, -1),
(math.sqrt(2), math.sqrt(2)),
(3, 4), # 3-4-5 triangle
# on the circle
(math.sqrt(2) / 2, math.sqrt(2) / 2), # pi/4 rads
(0.5, math.sqrt(3) / 2), # pi/3 rads
]

for x, y in POINTS:
h = math.hypot(x, y)
print('{:7.2f} {:7.2f} {:7.2f}'.format(x, y, h))

单位圆上的点总是斜边等于 1。

1
2
3
4
5
6
7
8
$ python3 math_hypot.py
## X Y Hypotenuse ------- ----------
1.00 1.00 1.41
-1.00 -1.00 1.41
1.41 1.41 2.00
3.00 4.00 5.00
0.71 0.71 1.00
0.50 0.87 1.00

该函数还可以被用来求两点之间的距离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# math_distance_2_points.py

import math

print('{:^8} {:^8} {:^8} {:^8} {:^8}'.format(
'X1', 'Y1', 'X2', 'Y2', 'Distance',
))
print('{:-^8} {:-^8} {:-^8} {:-^8} {:-^8}'.format(
'', '', '', '', '',
))

POINTS = [
((5, 5), (6, 6)),
((-6, -6), (-5, -5)),
((0, 0), (3, 4)), # 3-4-5 triangle
((-1, -1), (2, 3)), # 3-4-5 triangle
]

for (x1, y1), (x2, y2) in POINTS:
x = x1 - x2
y = y1 - y2
h = math.hypot(x, y)
print('{:8.2f} {:8.2f} {:8.2f} {:8.2f} {:8.2f}'.format(
x1, y1, x2, y2, h,
))

使用 x 和 y 值的差将一个端点移至原点,然后将差传递给hypot()

1
2
3
4
5
6
$ python3 math_distance_2_points.py
## X1 Y1 X2 Y2 Distance -------- -------- -------- --------
5.00 5.00 6.00 6.00 1.41
-6.00 -6.00 -5.00 -5.00 1.41
0.00 0.00 3.00 4.00 5.00
-1.00 -1.00 2.00 3.00 5.00

math还定义了反三角函数。

1
2
3
4
5
6
7
8
9
# math_inverse_trig.py

import math

for r in [0, 0.5, 1]:
print('arcsine({:.1f}) = {:5.2f}'.format(r, math.asin(r)))
print('arccosine({:.1f}) = {:5.2f}'.format(r, math.acos(r)))
print('arctangent({:.1f}) = {:5.2f}'.format(r, math.atan(r)))
print()

1.57 约等于π/ 2 或 90 度,即正弦为 1 而余弦为 0 的角度。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ python3 math_inverse_trig.py

arcsine(0.0) = 0.00
arccosine(0.0) = 1.57
arctangent(0.0) = 0.00

arcsine(0.5) = 0.52
arccosine(0.5) = 1.05
arctangent(0.5) = 0.46

arcsine(1.0) = 1.57
arccosine(1.0) = 0.00
arctangent(1.0) = 0.79

双曲函数

双曲函数出现在线性微分方程中,在处理电磁场、流体力学、狭义相对论和其他高等物理和数学时使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# math_hyperbolic.py

import math

print('{:^6} {:^6} {:^6} {:^6}'.format(
'X', 'sinh', 'cosh', 'tanh',
))
print('{:-^6} {:-^6} {:-^6} {:-^6}'.format('', '', '', ''))

fmt = '{:6.4f} {:6.4f} {:6.4f} {:6.4f}'

for i in range(0, 11, 2):
x = i / 10.0
print(fmt.format(
x,
math.sinh(x),
math.cosh(x),
math.tanh(x),
))

余弦函数和正弦函数表示一个圆,而双曲余弦函数和双曲正弦函数表示双曲线的一半。

1
2
3
4
5
6
7
8
$ python3 math_hyperbolic.py
## X sinh cosh tanh ------ ------ ------
0.0000 0.0000 1.0000 0.0000
0.2000 0.2013 1.0201 0.1974
0.4000 0.4108 1.0811 0.3799
0.6000 0.6367 1.1855 0.5370
0.8000 0.8881 1.3374 0.6640
1.0000 1.1752 1.5431 0.7616

反双曲函数acosh()asinh()atanh()也可用。

特殊函数

高斯误差函数常用于统计。

1
2
3
4
5
6
7
8
9
# math_erf.py

import math

print('{:^5} {:7}'.format('x', 'erf(x)'))
print('{:-^5} {:-^7}'.format('', ''))

for x in [-3, -2, -1, -0.5, -0.25, 0, 0.25, 0.5, 1, 2, 3]:
print('{:5.2f} {:7.4f}'.format(x, math.erf(x)))

对于误差函数,erf(-x) == -erf(x).

1
2
3
4
5
6
7
8
9
10
11
12
13
$ python3 math_erf.py
## x erf(x) -------
-3.00 -1.0000
-2.00 -0.9953
-1.00 -0.8427
-0.50 -0.5205
-0.25 -0.2763
0.00 0.0000
0.25 0.2763
0.50 0.5205
1.00 0.8427
2.00 0.9953
3.00 1.0000

互补误差函数为1-erf(x)

1
2
3
4
5
6
7
8
9
# math_erfc.py

import math

print('{:^5} {:7}'.format('x', 'erfc(x)'))
print('{:-^5} {:-^7}'.format('', ''))

for x in [-3, -2, -1, -0.5, -0.25, 0, 0.25, 0.5, 1, 2, 3]:
print('{:5.2f} {:7.4f}'.format(x, math.erfc(x)))

erfc()的实现避免了从 1 减去 x 的小数值的精度误差。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ python3 math_erfc.py
## x erfc(x) -------
-3.00 2.0000
-2.00 1.9953
-1.00 1.8427
-0.50 1.5205
-0.25 1.2763
0.00 1.0000
0.25 0.7237
0.50 0.4795
1.00 0.1573
2.00 0.0047
3.00 0.0000

See also- Standard library documentation for math- IEEE floating point arithmetic in Python – Blog post by John Cook about how special values arise and are dealt with when doing math in Python.- SciPy – Open source libraryes for scientific and mathematical calculations in Python. - PEP 485 – “A function for testing approximate equality”


【译】Python 标准库教程——math 模块
https://guixinliu.github.io/2020/02/15/python-math/
发布于
2020年2月15日
许可协议