Skip to main content

数字

info

本提案定义了一个抽象基类(ABC)的层次结构(PEP 3119),用于表示类似数字的类。

它提议了一个层次结构,其中 Number :> Complex :> Real :> Rational :> Integral

PEP 3141 – 数字类型层次结构

存在三种不同的数字类型:整数、浮点数和复数。 此外,布尔值属于整数的子类型。

整数具有无限的精度。

浮点数通常使用 C 中的 double 来实现;有关你的程序运行所在机器上浮点数的精度和内部表示法可在 sys.float_info 中查看。

复数包含实部和虚部,分别以一个浮点数表示。 要从一个复数 z 中提取这两个部分,可使用 z.realz.imag

整个数字类(整数、浮点数、复数、布尔值)都属于不可变类型(Immutable Type),即一旦创建,其值不能被修改。当变量重新赋值时,其实指向了新的内存地址。

整数

初学者通常只掌握十进制整数的定义方法,其实 Python 定义整数时支持四种进制格式:

# 十进制整数除了单独的0外,不能以0开头,否则会报语法错误
a = 17
# 16进制 使用 0x 或 0X 前缀
b = 0x11
# 二进制 使用 0b 或 0B 前缀
c = 0b10001
# 八进制 使用 0o 或 0O 前缀
d = 0o21

print(a == b) # True
print(a == c) # True
print(a == d) # True

print(0x11 is 17) # True 驻留机制
print(0x11 is 0b10001) # True
print(0x11 is 0o21) # True

# 对于负数,定义时使用 - 前缀,正数定义时可省略 + 前缀
e = -17
f = -0x11
g = -0b10001
h = -0o21

print(e == f) # True
print(e == g) # True
print(e == h) # True

# 为了提高可读性,各进制都可以在数字中使用下划线 _ 分隔。
print(1_000_000) # 1000000
print(0b1010_1010) # 170
note

x 不总是等于 +x

在Python中,几乎所有情况下x 总是等于 +x

特殊情况1:自定义类

如果类重载了 __pos__ 方法,+x 的行为可以自定义,这时 x 可能不等于 +x

例如Python的collections.Counter类重载了 __pos__ 方法,所以+Counter() 的行为可以自定义。

特殊情况2:decimal Decimal 类型的“设计是基于考虑人类习惯的浮点数模型,并且因此具有以下最高指导原则 —— 计算机必须提供与人们在学校所学习的算术相一致的算术。” —— 摘自 decimal 算术规范描述。

Decimal 通过设置精度上下文,实现在精度内,数字可以完全精确地表示。

相比之下,1.1 和 2.2 这样的数字在二进制浮点形式下没有精确的表示。 最终用户通常不希望 1.1 + 2.2 像在二进制浮点形式下那样被显示为 3.3000000000000003。

import decimal

ctx = decimal.getcontext()
ctx.prec = 40 # 设置精度为40
one_third = decimal.Decimal('1') / decimal.Decimal('3')
print(one_third)

print(one_third == +one_third) # True

ctx.prec = 28 # 设置精度为28
print(one_third == +one_third) # False

int函数

可以通过int()函数将字符串或数字转换为整数。

int函数签名:int(x, base=10) -> int

参数说明:

  • x:要转换的字符串或数字
  • base:进制,默认为10

返回值:

  • 返回一个整数
# 将字符串转换为整数
print(int('123')) # 123

# 将浮点数转换为整数,会舍弃小数部分
print(int(123.55)) # 123

# 将字符串转换为十六进制整数,base表示进制
print(int('11', base=16)) # 17

浮点数

# 只要进行了除法运算,就会产生浮点数。即便除数、被除数都是整数,也没有余数,商也会是浮点数。
print(10 / 2) # 5.0

# 使用科学计数法定义的都是浮点数,e 或 E 表示 10 的幂次。
print(1e1) # 10.0
print(2.5e3) # 2500.0
print(1E-2) # 0.01

float函数

可以通过float()函数将字符串或数字转换为浮点数。

float函数签名:float(x) -> float

参数说明:

  • x:要转换的字符串或数字

返回值:

  • 返回一个浮点数
# 将字符串转换为浮点数
print(float('3.14')) # 3.14

# 将整数转换为浮点数
print(float(1)) # 1.0

tip

浮点数存在一些特殊值,包括无穷大、负无穷大和NaN。

无穷大表示超出浮点数表示范围的值,NaN表示未定义或无法表示的浮点运算结果。

无穷大的运算遵循数学运算规则,NaN参与的运算结果为NaN,但NaN不等于任何值,包括自己,实际业务中往往是剔除或众数补全。

# 无穷大
float('inf')
# 负无穷大
float('-inf')
# NaN
float('nan')

# NaN与任何值(包括自己)都不相等,也是唯一不等于自身的值,
print(float('nan') == float('nan')) # False
print(float('nan') is float('nan')) # False

# 可以通过这点判断是否为NaN
is_nan = lambda x: x != x
print(is_nan(float('nan'))) # True

精度问题

浮点数精度问题源于二进制无法精确表示某些十进制的小数部分,这并非 Python 语言的问题,是由于二进制浮点表示的固有限制。

Python 的浮点数遵循 IEEE 754 双精度标准,使用64位存储,下面的代码输出清楚地说明了这一点。

import sys
print(sys.float_info)
"""
sys.float_info(
# 表示当前系统能够表示的最大浮点数,值为 1.7976931348623157e+308。这是双精度浮点数的最大值(IEEE 754 标准)。
max=1.7976931348623157e+308,
# 表示浮点数能够表示的最大指数值(以 2 为底),值为 1024。这是双精度浮点数的最大指数值。
max_exp=1024,
# 表示浮点数能够表示的最大指数值(以 10 为底),值为 308。这是双精度浮点数在十进制下的最大指数值。
max_10_exp=308,
# 表示当前系统能够表示的最小正浮点数(非零),值为 2.2250738585072014e-308。这是双精度浮点数的最小正数。
min=2.2250738585072014e-308,
# 表示浮点数能够表示的最小指数值(以 2 为底),值为 -1021。这是双精度浮点数的最小指数值。
min_exp=-1021,
# 表示浮点数能够表示的最小指数值(以 10 为底),值为 -307。这是双精度浮点数在十进制下的最小指数值。
min_10_exp=-307,
# 表示浮点数在十进制下的有效数字位数,值为 15。这意味着双精度浮点数可以精确表示大约 15 位十进制数字。
dig=15,
# 表示浮点数的尾数位数(以 2 为底),值为 53。这是双精度浮点数的尾数位数(包括隐含的 1)。
mant_dig=53,
# 表示 1.0 和下一个可表示的浮点数之间的差值,值为 2.220446049250313e-16。这是双精度浮点数的机器精度。
epsilon=2.220446049250313e-16,
# 表示浮点数的基数(进制),值为 2。这意味着浮点数是基于二进制的。
radix=2,
# 表示浮点数的舍入模式,值为 1。这表示当前系统使用的是“舍入到最近偶数”的舍入模式(IEEE 754 默认模式)
rounds=1
)
"""

# 经典的浮点数精度问题
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False

# 查看实际存储的值
from decimal import Decimal
print(Decimal(0.1)) # 0.1000000000000000055511151231257827021181583404541015625
print(Decimal(0.2)) # 0.200000000000000011102230246251565404236316680908203125

# 更复杂的例子
total = 0.0
for i in range(10):
total += 0.1
print(total) # 0.9999999999999999
print(total == 1.0) # False

# 因此永远不要直接比较浮点数是否相等,使用容差比较(允许容纳极小的误差)
def robust_equals(a, b, rel_tol=1e-09, abs_tol=1e-12):
"""
比较两个数值是否在给定的相对或绝对误差范围内相等。

参数:
a (float): 待比较的第一个数值。
b (float): 待比较的第二个数值。
rel_tol (float, optional): 相对误差容限,用于处理较大数值时的比例误差。默认为 1e-09。
abs_tol (float, optional): 绝对误差容限,用于处理接近零时的微小误差。默认为 1e-12。

返回:
bool: 如果 `a` 和 `b` 在给定的误差范围内相等,则返回 `True`,否则返回 `False`。

示例:
>>> robust_equals(1.0, 1.000000001)
True
>>> robust_equals(1.0, 1.0001)
False
"""
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

print(robust_equals(1.0, 1.0001)) # False
# 等价于使用内置的isclose函数(推荐)
import math
print(math.isclose(1.0, 1.0001, rel_tol=1e-09, abs_tol=1e-12)) # False

浮点数陷阱与解决方案

浮点数所有陷阱的根源都是因为浮点数无法精确表示所有数,因此需要使用其他方式来存储数据,最常见的解决方案是使用Decimal类型、字符串或整数。

import json
from decimal import Decimal

# 陷阱1: JSON序列化精度丢失
def json_precision_issue():
# 前因:字符串转Json、float会出现精度丢失现象,因此需要使用精度无限的整数或字符串来存储数据
response = '{"abc": 4.4257052820783003}'

print(float("4.4257052820783003")) # 4.4257052820783 精度丢失
print(json.loads(response)) # {'abc': 4.4257052820783} 精度丢失
print(json.dumps(json.loads(response))) # 精度丢失是永久的,之后无法还原

# 推荐写法
class DecimalEncoder(json.JSONEncoder):
"""重新序列化Decimal需要自定义编码器"""
def default(self, obj):
if isinstance(obj, Decimal):
# 将Decimal转换为字符串,精度不会丢失 如果使用float(obj)依然会丢失精度
return str(obj)
return super().default(obj)
# 使用Decimal解析字符串,精度不会丢失
decimal_json_str = json.loads(response, parse_float=Decimal)
# 使用自定义编码器重新序列化,确保转化后的数据精度不会丢失
print(json.dumps(decimal_json_str, cls=DecimalEncoder))
# {"abc": "4.4257052820783003"} 这是精度保持正常的输出结果

# 陷阱2: 循环累加误差
def accumulation_error_demo():
"""循环累加导致的误差积累"""
# 错误的方式
total = 0.0
for _ in range(1000000):
total += 0.1
print(f"Wrong way: {total}") # 不等于100000.0

# 正确的方式1:使用整数运算
total_cents = 0
for _ in range(1000000):
total_cents += 10 # 以分为单位
total = total_cents / 100
print(f"Correct way 1: {total}")

# 正确的方式2:使用Decimal
total = Decimal('0')
for _ in range(1000000):
total += Decimal('0.1')
print(f"Correct way 2: {total}")

# 陷阱3: 浮点数作为字典键
def float_key_problem():
"""浮点数作为字典键的问题"""
d = {}
key1 = 0.1 + 0.2
key2 = 0.3

d[key1] = "value1"
print(d.get(key2)) # None,因为key1 != key2

# 解决方案:使用精度无限的字符串或整数
d_safe = {}
d_safe[str(round(key1, 10))] = "value1"
print(d_safe.get(str(round(key2, 10)))) # "value1"

# 陷阱4: 范围判断
def range_check_issue():
"""浮点数范围判断的陷阱"""
values = []
x = 0.0
while x <= 1.0:
values.append(x)
x += 0.1

print(f"Values count: {len(values)}") # 是11个而不是期望的10个
print(f"Last value: {values[-1]}") # 0.9999999999999999
print(f"Last value <= 1.0: {values[-1] <= 1.0}") # True

复数

复数(Complex Number)是数学中的一种数,表示为z=a+ibz=a+ib

其中ab是实数,i是虚数单位,是可以与实数在一起按照同样的运算律进行四则运算,满足i2=1i^2=-1的数。

复数是Python的内置数字类型,在科学计算、信号处理、控制系统等领域有广泛应用。这里只介绍复数的基本操作。

complex函数

complex函数签名:complex(real, imag=0) -> complex

参数说明:

  • real:实部
  • imag:虚部,默认为0

返回值:

  • 返回一个复数
# 使用complex()函数创建复数
z1 = complex(3, 4) # 3+4j
z2 = complex('3+4j') # 从字符串创建
z3 = complex(3) # 3+0j

# 使用j或J作为虚数单位
z4 = 3 + 4j
z5 = 3 + 4J # j和J等价
z6 = 4j # 纯虚数
z7 = 3 + 0j # 实数的复数表示

复数的属性和基本操作

z = 3 + 4j

# 基本属性
print(f"实部: {z.real}") # 3.0
print(f"虚部: {z.imag}") # 4.0
print(f"共轭: {z.conjugate()}") # (3-4j)

布尔类型

布尔类型是整数类型的子类,只有True和False两种值。

bool函数

bool(x) 函数可以将任意值转换为布尔值。

函数签名:bool(x) -> bool

参数说明:

  • x:要转换的值

返回值:

  • 返回一个布尔值

如果x是数字类型,则判断x是否为0,为0则返回False,否则返回True。

如果x是布尔类型,则返回x

默认情况下,用户定义的类的实例都为True。除非实现了__bool____len__方法。

简单来说,bool(x)调用x.__bool__()方法,如果__bool__方法返回False,则bool(x)返回False,否则返回True。

如果没有实现__bool__方法,则调用__len__方法,如果__len__方法返回0,则bool(x)返回False,否则返回True。

常见的布尔值为False的情况:

# 是数字类型且为0的值
print(bool(0)) # False
print(bool(0.0)) # False
print(bool(0j)) # False

# 是布尔类型且为False的值
print(bool(False)) # False

# 实现了__bool__方法且返回False的值
print(bool(None)) # False

# 没有实现__bool__方法且__len__方法返回0的值,例如:
print(bool("")) # False - 空字符串
print(bool([])) # False - 空列表
print(bool({})) # False - 空字典
print(bool(set())) # False - 空集合

其他的数值布尔值为True。

布尔运算

and运算真值表
ABA and B
TrueTrueTrue
TrueFalseFalse
FalseTrueFalse
FalseFalseFalse
or运算真值表
ABA or B
TrueTrueTrue
TrueFalseTrue
FalseTrueTrue
FalseFalseFalse
not运算真值表
Anot A
TrueFalse
FalseTrue

布尔类型数字性质

# 布尔值参与运算时,可被当作整数使用,True为1,False为0
print(True is 1) # False 因为True和1是不同的对象
print(True == 1) # True 因为True和1的值相等

print(True + True) # 2
print(False * 10) # 0
print(True / 2) # 0.5

# 布尔值的类型关系
print(isinstance(True, int)) # True
print(isinstance(True, bool)) # True
print(type(True)) # <class 'bool'>

# 布尔值在列表中的计数
values = [True, False, True, True, False]
true_count = sum(values) # 3
false_count = len(values) - true_count # 2
print(f"True的个数: {true_count}, False的个数: {false_count}")

内置函数

abs函数

返回一个数字的绝对值。 参数可以是整数、浮点数或任何实现了 abs() 的对象。

函数签名:abs(x) -> number

参数说明:

  • x:要计算绝对值的数字

返回值:

  • 返回一个数字的绝对值。如果参数是一个复数,则返回它的模。
print(abs(-10))     # 10
print(abs(10.5)) # 10.5
print(abs(3+4j)) # 5.0 - 复数的模长,即sqrt(3²+4²)

max函数、min函数

二者语法一致、参数一致,只是行为相反。

max函数签名:max(arg1, arg2, *args, key=None) min函数签名:min(arg1, arg2, *args, key=None)

参数说明:

  • arg1, arg2, *args:要比较的参数,可以是多个
  • key:一个函数,用于指定比较的规则,默认为None,即直接比较

返回值:

  • max返回可迭代对象中的最大值,min返回可迭代对象中的最小值
print(max(1, 2, 3)) # 3
print(max([1], [2], [3])) # [3]
print(max([[1]], [[2]], [[3]])) # [[3]]

可以通过指定key参数,来自定义比较规则。例如,绝对值。

lists = [-2,1]
print(max(lists)) # 1

def _sort(value):
return abs(value)
print(max(lists,key=_sort)) # -2

sum函数

返回可迭代对象中所有元素的和。

函数签名:sum(iterable, /, start=0)

参数说明:

  • iterable:要计算和的可迭代对象
  • start:要添加到和的初始值,默认为0
print(sum((1, 2, 3))) # 6

print(sum([1,2,3],10)) # 16

round函数

四舍五入函数,如果与两个倍数同样接近,则选用偶数。

函数签名:round(number, ndigits=None)

参数说明:

  • number:要四舍五入的数字
  • ndigits:要保留的小数位数,默认为None,即四舍五入到整数

返回值:

  • 返回一个四舍五入后的数字,round(0.5)round(-0.5) 均得出 0round(1.5) 则为 2
# 四舍五入转化
print(round(123.55)) # 124

# 四舍五入转化,并指定小数位数
print(round(123.555,2)) # 123.56
tip

浮点数执行 round() 的行为可能会令人惊讶:例如,round(2.675, 2) 将给出 2.67 而不是期望的 2.68。

这不算是程序错误:这一结果是由于大多数十进制小数实际上都不能以浮点数精确地表示。

divmod函数

以两个(非复数)数字为参数,在作整数除法时,返回商和余数。

若操作数为混合类型,则适用二进制算术运算符的规则。

函数签名:divmod(a, b) -> tuple

参数说明:

  • a:被除数
  • b:除数

返回值:

  • 返回一个包含商和余数的元组
print(divmod(7, 3)) # (2, 1)

print(divmod(6, 3)) # (2, 0)

print(divmod(6.0, 3.0)) # (2.0, 0.0)

print(divmod(-6, 3)) # (-2, 0)

print(divmod(6.5, 3)) # (2.0, 0.5)

pow函数

函数签名:pow(base, exp, mod=None)

参数说明:

  • base:底数
  • exp:指数
  • mod:模数,默认为None,即不进行取模

返回值:

  • 返回baseexp次幂。如果mod存在,则再对结果进行取模,其结果等效于pow(base, exp) % mod

等价于base ** exp,如果mod存在,则再对结果进行取模,其结果等效于(base ** exp) % mod

print(pow(2, 3)) # 8
# 等价于
print(2**3) # 8

print(pow(2, 3, 5)) # 3
# 等价于
print((2**3) % 5) # 3

相关库推荐

  • random: 生成伪随机数
  • math: 数学函数
  • decimal: 十进制定点和浮点算术
  • numbers: 数值抽象基类

  • cmath: 复数数学函数

  • fractions: 有理数

  • statistics: 数学统计函数