17. 整数、浮点数和复数

Python 的数字对象是具有专门实现的普通对象。它们参与与列表、字典、函数、类和模块相同的对象模型:每个值都有一个对象头、一个类型指针、引用计数行为和用于操作的类型槽。

主要的内置数字类型有:

|类型 | Python 名称 |主要代表| | -------------- | ----------- | ------------------------ | | |整数|int|任意精度整数 | |布尔 |bool|单例子类int| |浮点数|float| C双| |复杂|complex|一对C双打|

这些类型在 Python 级别看起来很简单,但每种类型都具有重要的运行时权衡。

17.1 数字对象就是对象

Python 整数通常不会作为原始 CPU 整数存储在 Python 变量中。python x = 42 在 CPython 级别,x指的是Python整型对象。

从概念上讲:text x ---> PyLongObject object header integer payload 对于浮点数和复数也是如此。python a = 1.5 b = 1 + 2j 从概念上讲:```text a ---> PyFloatObject object header double value

b ---> PyComplexObject object header double real double imag 对象模型为数字提供了正常的 Python 行为:python print(type(42)) print((42).class) print((42).bit_length())


## 17.2 整数对象

蟒蛇`int`是任意精度。```python
x = 10 ** 100
print(x)
```该值在 32  64 位时不会溢出。 CPython 根据需要增长内部表示。

C级类型通常称为`PyLongObject`

从概念上讲:```text
PyLongObject
    PyVarObject header
        ob_size
    digit array
````ob_size`字段对内部位数和符号进行编码。数字数组将绝对值存储在一个大基数中。

这就是为什么 Python 可以计算:```python
x = 2 ** 1000
y = x * x
print(y)
```没有整数溢出。

代价是大整数比机器整数需要更多的内存和更多的 CPU 时间。

## 17.3 整数数字存储

CPython 将整数存储为多个内部数字。

简化模型:```text
value = sign * (d0 + d1 * base + d2 * base^2 + ...)
```对于小整数,可能只需要一位数字。

对于大整数,需要很多位数。

从概念上讲:```text
42
    sign = positive
    digits = [42]

2**1000
    sign = positive
    digits = [d0, d1, d2, ...]
```这种表示方式类似于人类将十进制数写成数字的方式,只不过 CPython 使用更大的内部基数来提高效率。

对这些数字数组进行算术运算。```text
small int addition
    cheap

large int addition
    cost grows with number of digits

large int multiplication
    cost grows more quickly, with specialized algorithms for large cases
```## 17.4 整数符号

该符号通过尺寸元数据表示,而不是作为单独的 Python 对象。

从概念上讲:```text
ob_size > 0
    positive integer

ob_size == 0
    zero

ob_size < 0
    negative integer
```数字数组存储幅度。

例子:```text
123
    ob_size = positive digit count
    digits = magnitude

-123
    ob_size = negative digit count
    digits = magnitude
```这种表示使对象保持紧凑。

## 17.5 整数不变性

整数是不可变的。```python
x = 10
x += 1
```这不会改变整数对象`10`。它创建或检索另一个整数对象并重新绑定`x`

从概念上讲:```text
x ---> int object 10

x += 1

x ---> int object 11
```当对象共享时,此行为是可见的:```python
a = 10
b = a

a += 1

print(a)    # 11
print(b)    # 10

b仍然引用原始整数对象。

17.6 小整数重用

CPython 重用了一些小的整数对象。```python a = 1 b = 1


这是一个优化。它减少了对共同价值观的分配。

不要依赖身份来进行整数值比较。

正确的:```python
if x == 1:
    ...
```错误:```python
if x is 1:
    ...

is测试对象身份。==测试数字相等。

17.7 布尔对象bool是一个子类int。```python

print(isinstance(True, int)) # True print(True + True) # 2 恰好有两个布尔单例对象:python True False 类型关系为:text bool subclass of int 这是为了历史和实际的兼容性而存在的。布尔值在数字上下文中工作,但代码应该将它们用作真值。python if ready: ... 而不是:python if ready == True: ... 在 C 级别,扩展代码通常返回布尔值:c Py_RETURN_TRUE; Py_RETURN_FALSE;


## 17.8 真值测试

数字对象参与真值测试。```python
bool(0)        # False
bool(1)        # True
bool(-1)       # True
bool(0.0)      # False
bool(0j)       # False
```数字类型的规则很简单:```text
zero value
    false

nonzero value
    true
```真实性测试使用类型协议机制。在 C 级别,数字类型通过槽提供行为,例如布尔转换。

## 17.9 整数运算

常见的整数运算包括:```python
a + b
a - b
a * b
a // b
a % b
a ** b
a << n
a >> n
a & b
a | b
a ^ b
~a
```这些映射到数字槽。

从概念上讲:```text
PyLong_Type
    nb_add
    nb_subtract
    nb_multiply
    nb_floor_divide
    nb_remainder
    nb_power
    nb_lshift
    nb_rshift
    nb_and
    nb_or
    nb_xor
    nb_invert
```每个操作接收Python对象并返回一个Python对象。

即使结果适合机器字,结果也是一个 Python 对象。

## 17.10 整数除法

Python 有两个主要的除法运算符。```python
a / b     # true division
a // b    # floor division
```对于整数:```python
print(5 / 2)     # 2.5
print(5 // 2)    # 2

/返回普通整数的浮点数。//返回楼层结果。

对于负值:python print(-5 // 2) # -3 print(-5 % 2) # 1 身份持有:text a == (a // b) * b + (a % b) %携带 Python 语义所需的符号约定。

17.11 位运算

整数支持位运算,就像以具有无限符号扩展的二进制补码表示一样。python x & y x | y x ^ y ~x x << n x >> n 例子:python print(5 & 3) # 1 print(5 | 3) # 7 print(5 ^ 3) # 6 print(~5) # -6 结果~x如下:```text ~x == -x - 1


## 17.12 整数方法

整数方法公开内部值的有用属性。```python
x = 1024

print(x.bit_length())
print(x.bit_count())

bit_length返回表示绝对值所需的位数。```python print((0).bit_length()) # 0 print((1).bit_length()) # 1 print((2).bit_length()) # 2 print((3).bit_length()) # 2


`bit_count`返回人口数量的绝对值。```python
print((7).bit_count())      # 3
```这些方法通常可以有效地映射到可用的内部数字运算和 CPU 内在函数

## 17.13 整数转换为字节

整数可以与字节序列相互转换。```python
x = 1024

data = x.to_bytes(2, byteorder="big")
print(data)

again = int.from_bytes(data, byteorder="big")
print(again)
```这对于:```text
binary protocols
cryptography
file formats
network byte order
serialization
```字节顺序必须是明确的。```python
x.to_bytes(4, "big")
x.to_bytes(4, "little")
```相同的整数根据字节顺序产生不同的字节布局

## 17.14 浮动对象

蟒蛇`float`通常是 C double 的包装

从概念上讲:```text
PyFloatObject
    PyObject header
    double value
```浮动对象是固定大小的。```python
x = 1.5
y = 2.25
print(x + y)
```浮点运算是通过数字槽和平台浮点运算来实现的

关键点浮点数是近似的二进制浮点数

## 17.15 二进制浮点

小数分数通常无法用二进制精确表示。```python
print(0.1 + 0.2)
```这通常打印:```text
0.30000000000000004
```问题在于代表性。`0.1`没有有限的二进制展开式就像`1/3`没有有限的十进制展开式

所以存储的值是最接近的可表示的二进制浮点数

这会影响平等:```python
print(0.1 + 0.2 == 0.3)    # False
```使用基于容差的比较来进行近似数值工作:```python
import math

math.isclose(0.1 + 0.2, 0.3)
```## 17.16 浮点特殊值

浮点数包含特殊值:```python
inf = float("inf")
nan = float("nan")
neg_inf = float("-inf")
```在许多比较中Infinity 的表现都符合预期:```python
print(inf > 1e308)     # True
```NaN 是不寻常的:```python
nan = float("nan")

print(nan == nan)      # False
print(nan != nan)      # True
```NaN 的意思是不是数字”。它与自身相比并不等于

此行为遵循浮点规则而不是普通的对象标识逻辑

## 17.17 浮点散列和相等

数字类型协调相等性和散列。```python
print(1 == 1.0)          # True
print(hash(1) == hash(1.0))
```如果两个数值对象比较相等则它们必须具有相同的哈希值

这使得字典能够正确运行:```python
d = {}
d[1] = "int"
d[1.0] = "float"

print(d)
```第二个作业更新了相同的关键位置因为`1 == 1.0`。

这种跨类型的数字相等很方便但它可能会让那些期望类型不同的键的用户感到惊讶

## 17.18 浮点转换

换算:```python
float(1)
int(1.9)
round(1.9)

int向零截断:```python print(int(1.9)) # 1 print(int(-1.9)) # -1


`round`遵循 Python 的舍入规则:```python
print(round(2.5))
print(round(3.5))
```如果浮点不能准确表示预期值则浮点到整数的转换可能会丢失信息

对于十进制金融算术请使用`decimal.Decimal`而不是二进制浮点数

## 17.19 复杂对象

蟒蛇`complex`存储两个浮点值:```text
PyComplexObject
    PyObject header
    double real
    double imag
```例子:```python
z = 3 + 4j

print(z.real)
print(z.imag)
```实部和虚部是浮点数

复数支持算术:```python
a = 1 + 2j
b = 3 + 4j

print(a + b)
print(a * b)
```他们不支持订购:```python
(1 + 2j) < (3 + 4j)    # TypeError
```Python 数值模型中的复数没有自然的全序

## 17.20 复杂算术

复数乘法如下:```text
(a + bi)(c + di) = (ac - bd) + (ad + bc)i
```Python 在复杂类型实现中处理这个问题

例子:```python
a = 1 + 2j
b = 3 + 4j

print(a * b)      # (-5+10j)
```部门和权力也得到支持

对于高级数值工作,`cmath`模块提供复杂感知的数学函数。```python
import cmath

print(cmath.sqrt(-1))
```## 17.21 数字塔

Python 的数值模型有一个概念层次结构:```text
numbers.Number
    numbers.Complex
        numbers.Real
            numbers.Rational
                numbers.Integral
```内置类型大致如下

|类型 |概念角色|
| --------- | -------------------------------------- |
|`complex`|复杂|
|`float`|真实|
|`int`|积分 |
|`bool`|实践中的积分子类 |

`numbers`模块为数字协议提供抽象基类

大多数普通 CPython 操作使用具体类型槽而不是抽象基类分派但层次结构有助于定义预期行为

## 17.22 混合数字运算

混合数字运算遵循强制和调度规则。```python
print(1 + 2.5)       # 3.5
print(1 + 2j)        # (1+2j)
print(1.5 + 2j)      # (3.5+2j)
```典型加宽方向:```text
int -> float -> complex
```这是一个概念模型实际的实现使用操作数类型之间的二元运算调度

对于用户定义的数字类返回`NotImplemented`让另一个操作数尝试反射的行为。```python
class N:
    def __add__(self, other):
        return NotImplemented
```## 17.23`__index__`

`__index__`意味着一个对象可以被解释为用于索引和切片的精确整数。```python
class Index:
    def __index__(self):
        return 2

xs = [10, 20, 30, 40]
print(xs[Index()])
```这比`__int__`。

用例包括:```text
list indexes
slice bounds
range arguments
bit operations
low-level integer contexts
``` C 级别这对应于整数索引协议行为

## 17.24`__int__`, `__float__`, 和`__complex__`转换方法允许自定义类似数字的对象转换为内置数字类型。```python
class Value:
    def __int__(self):
        return 10

    def __float__(self):
        return 10.5

    def __complex__(self):
        return 10.5 + 2j
```用法:```python
v = Value()

print(int(v))
print(float(v))
print(complex(v))
```这些转换创建内置数字对象

它们不会自动将自定义类型设为完全数字算术方法仍然需要单独实现

## 17.25 数字槽

数字行为是通过槽实现的

概念槽表:```text
PyNumberMethods
    nb_add
    nb_subtract
    nb_multiply
    nb_remainder
    nb_divmod
    nb_power
    nb_negative
    nb_positive
    nb_absolute
    nb_bool
    nb_invert
    nb_lshift
    nb_rshift
    nb_and
    nb_xor
    nb_or
    nb_int
    nb_float
    nb_index
    nb_matrix_multiply
    nb_inplace_add
    ...
```Python 语法映射到此表中

|语法 |老虎机概念|
| ---------- | -------------------- |
|`a + b`|加法|
|`a - b`|减法|
|`a * b`|乘法|
|`a @ b`|矩阵乘法 |
|`-a`|否定|
|`abs(a)`|绝对值|
|`bool(a)`|真值|
|`int(a)`|整数转换|
|`float(a)`|浮点数转换|
|`a << b`|左移|

这个槽表就是为什么内置和扩展数字类型可以与 Python 语法集成的原因

## 17.26 就地数值运算

就地运算符包括:```python
x += y
x -= y
x *= y
x //= y
x **= y
```对于不可变数字就地操作创建一个新对象并重新绑定名称。```python
x = 10
before = id(x)

x += 1
after = id(x)

print(before == after)   # usually False
```对于可变的类似数字的对象类型可以实现真正的就地突变

内置`int`, `float`, `complex`是不可变的

## 17.27 小数和分数

标准库提供了核心内置函数之外的数字类型。`decimal.Decimal`提供十进制浮点运算。```python
from decimal import Decimal

print(Decimal("0.1") + Decimal("0.2"))

fractions.Fraction提供有理算术。```python from fractions import Fraction

print(Fraction(1, 3) + Fraction(1, 6))


当它们的语义与问题匹配时使用它们:

|需要|类型 |
| ----------------------------------- | ---------- |
|一般整数 |`int`|
|近似科学数值工作|`float`|
|复杂算术 |`complex`|
|十进制金融算术|`Decimal`|
|精确有理算术 |`Fraction`|

## 17.28 C API:创建数字

创建一个整数:```c
PyObject *x = PyLong_FromLong(42);
if (x == NULL) {
    return NULL;
}
```从更广泛的 C 类型创建:```c
PyObject *x = PyLong_FromLongLong(value);
```创建浮动:```c
PyObject *f = PyFloat_FromDouble(3.14);
if (f == NULL) {
    return NULL;
}
```创建一个综合体:```c
PyObject *z = PyComplex_FromDoubles(1.0, 2.0);
if (z == NULL) {
    return NULL;
}
```每个都返回一个新的引用。调用者拥有它。

## 17.29 C API:提取数字

提取 C 长:```c
long value = PyLong_AsLong(obj);
if (value == -1 && PyErr_Occurred()) {
    return NULL;
}
```错误检查很重要,因为`-1`可以是一个有效的结果。

提取双精度数:```c
double value = PyFloat_AsDouble(obj);
if (value == -1.0 && PyErr_Occurred()) {
    return NULL;
}
```再次检查异常状态。

对于类似整数的上下文,当需要精确的整数语义时,请使用索引转换 API

## 17.30 C 边界溢出

Python 整数是任意精度的。 C 整数是固定宽度的。

此转换可能会失败:```c
long value = PyLong_AsLong(obj);
```如果`obj`对于 C 来说太大`long`

Python级别的例子:```python
x = 10 ** 100
```该值作为 Python 有效`int`,但可能不适合任何 C 整数类型。

C 扩展代码必须处理溢出错误。

一个常见的错误是假设 Python`int`总是适合`int`, `long` 或者`size_t`

## 17.31 性能说明

数字表现取决于代表性。

小整数算术已优化,但仍可在 Python 对象上运行。

紧密循环:```python
total = 0

for i in range(10_000_000):
    total += i
```执行许多 Python 级别的操作:```text
load total
load i
integer addition
create or retrieve result integer
store total
loop control
```这比原始机器整数上的等效 C 慢得多。

对于大型数值数组,请使用紧凑地存储原始值的专用库,例如`array`, `memoryview`,或 NumPy

Python 的内置数值类型优化标量语义,而不是密集数值向量计算。

## 17.32 记忆笔记

整数列表存储对整数对象的引用。```python
xs = [1, 2, 3, 4]
```从概念上讲:```text
list
    [ptr][ptr][ptr][ptr]
      |    |    |    |
      v    v    v    v
     int  int  int  int
```数组存储原始值:```python
from array import array

xs = array("i", [1, 2, 3, 4])
```从概念上讲:```text
array
    [int][int][int][int]
```这就是为什么数组对于大型同质数值数据来说内存效率更高。

## 17.33 常见的数字陷阱

|陷阱|示例|更好的方法|
| -------------------------------------- | ------------------------------------------- | ------------------------ | |
|使用`is`对于数字|`x is 1000`                     | `x == 1000`|
|期望 float | 的小数精度`0.1 + 0.2 == 0.3`              | `math.isclose`或者`Decimal`|
|坚持`hash()`                    | `hash(x)`作为稳定ID |`hashlib`|
|假设 Python int 适合 C long |`PyLong_AsLong`未经检查 |检查溢出|
|将列表用于密集数值数组 |`[0] * n`对于巨大的数字数据|`array` NumPy |
|正常比较 NaN |`nan == nan`|使用`math.isnan`|
|忽略整数增长成本 |热路径中的巨大力量|考虑算法边界 |

## 17.34 心智模型

使用这个模型:```text
int
    immutable arbitrary-precision integer
    variable-size digit array
    no fixed overflow
    cost grows with magnitude

bool
    singleton subclass of int
    truth-value type
    values are True and False

float
    immutable wrapper around C double
    approximate binary floating-point
    supports inf and nan

complex
    immutable pair of doubles
    real and imaginary parts
    arithmetic supported
    ordering unsupported
``` CPython 级别:```text
numeric operation
    dispatch through type slots
    operate on concrete numeric representation
    allocate or return result object
    manage references
```## 17.35 总结

CPython 将整数、浮点数和复数实现为具有专门 C 布局的 Python 对象。`int`使用任意精度存储,因此可以避免固定宽度溢出,但会付出随值大小而增长的成本。`float`包装 C double 并继承二进制浮点行为。`complex`存储两个双精度数并支持复杂算术。

这些数字类型是不可变的。操作创建结果对象而不是改变操作数。它们的行为通过数字槽、转换协议、散列规则和 C API 函数进行集成。