17. Số nguyên, số float và số phức
17. Số nguyên, số float và số phức
Các đối tượng số của Python là các đối tượng thông thường được triển khai chuyên biệt. Chúng tham gia vào cùng một mô hình đối tượng như danh sách, từ điển, hàm, lớp và mô-đun: mỗi giá trị có một tiêu đề đối tượng, một con trỏ kiểu, hành vi đếm tham chiếu và các vùng kiểu cho các phép toán.
Các loại số tích hợp chính là:
| Loại | Tên Python | Đại diện chính |
|---|---|---|
| Số nguyên | int |
Số nguyên có độ chính xác tùy ý |
| Boolean | bool |
Lớp con Singleton củaint |
| Điểm nổi | float |
C đôi |
| Phức tạp | complex |
Cặp C đôi |
Các loại này trông đơn giản ở cấp độ Python, nhưng mỗi loại đều có sự cân bằng quan trọng trong thời gian chạy.
17.1 Đối tượng số là đối tượng
Số nguyên Python thường không được lưu trữ dưới dạng số nguyên CPU thô trong các biến Python.python x = 42 Ở cấp độ CPython,xđề cập đến một đối tượng số nguyên Python.
Về mặt khái niệm:text x ---> PyLongObject object header integer payload Điều này cũng đúng với số float và số phức.python a = 1.5 b = 1 + 2j Về mặt khái niệm:```text
a ---> PyFloatObject
object header
double value
b ---> PyComplexObject
object header
double real
double imag
Mô hình đối tượng cung cấp cho các số hành vi Python bình thường:python
print(type(42))
print((42).class)
print((42).bit_length())
## 17.2 Đối tượng số nguyên
Python`int`là độ chính xác tùy ý.```python
x = 10 ** 100
print(x)
```Giá trị không tràn ở 32 hoặc 64 bit. CPython phát triển đại diện nội bộ khi cần thiết.
Loại cấp C thường được gọi là`PyLongObject`.
Về mặt khái niệm:```text
PyLongObject
PyVarObject header
ob_size
digit array
```các`ob_size`trường mã hóa cả số chữ số bên trong và dấu. Mảng chữ số lưu trữ giá trị tuyệt đối trong một cơ sở lớn.
Đây là lý do tại sao Python có thể tính toán:```python
x = 2 ** 1000
y = x * x
print(y)
```không có tràn số nguyên.
Cái giá phải trả là số nguyên lớn đòi hỏi nhiều bộ nhớ hơn và nhiều thời gian CPU hơn số nguyên máy.
## 17.3 Lưu trữ chữ số nguyên
CPython lưu trữ một số nguyên dưới dạng nhiều chữ số bên trong.
Một mô hình đơn giản hóa:```text
value = sign * (d0 + d1 * base + d2 * base^2 + ...)
```Đối với số nguyên nhỏ, chỉ cần một chữ số.
Đối với số nguyên lớn, cần có nhiều chữ số.
Về mặt khái niệm:```text
42
sign = positive
digits = [42]
2**1000
sign = positive
digits = [d0, d1, d2, ...]
```Cách biểu diễn này tương tự như cách con người viết số thập phân dưới dạng chữ số, ngoại trừ CPython sử dụng cơ số nội bộ lớn hơn nhiều để đạt hiệu quả.
Các phép toán số học hoạt động trên các mảng chữ số này.```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 Dấu nguyên
Dấu hiệu được thể hiện thông qua siêu dữ liệu kích thước, không phải dưới dạng đối tượng Python riêng biệt.
Về mặt khái niệm:```text
ob_size > 0
positive integer
ob_size == 0
zero
ob_size < 0
negative integer
```Mảng chữ số lưu trữ độ lớn.
Ví dụ:```text
123
ob_size = positive digit count
digits = magnitude
-123
ob_size = negative digit count
digits = magnitude
```Biểu diễn này giữ cho đối tượng nhỏ gọn.
## 17.5 Tính bất biến của số nguyên
Số nguyên là bất biến.```python
x = 10
x += 1
```Điều này không làm thay đổi đối tượng số nguyên`10`. Nó tạo hoặc truy xuất một đối tượng số nguyên khác và liên kết lại`x`.
Về mặt khái niệm:```text
x ---> int object 10
x += 1
x ---> int object 11
```Hành vi này hiển thị khi các đối tượng được chia sẻ:```python
a = 10
b = a
a += 1
print(a) # 11
print(b) # 10
bvẫn đề cập đến đối tượng số nguyên ban đầu.
17.6 Tái sử dụng số nguyên nhỏ
CPython tái sử dụng một số đối tượng số nguyên nhỏ.```python a = 1 b = 1
Đây là một sự tối ưu hóa. Nó làm giảm sự phân bổ cho các giá trị chung.
Không dựa vào danh tính để so sánh giá trị số nguyên.
Chính xác:```python
if x == 1:
...
```Không đúng:```python
if x is 1:
...
iskiểm tra danh tính đối tượng.==kiểm tra đẳng thức số.
17.7 Đối tượng Booleanboollà một lớp con củaint.
print(isinstance(True, int)) # True
print(True + True) # 2
```Có chính xác hai đối tượng singleton boolean:```python
True
False
```Mối quan hệ loại là:```text
bool
subclass of int
```Điều này tồn tại vì sự tương thích lịch sử và thực tế. Các giá trị Boolean hoạt động trong ngữ cảnh số, nhưng mã nên sử dụng chúng cho các giá trị chân lý.```python
if ready:
...
```Còn hơn là:```python
if ready == True:
...
```Ở cấp độ C, mã mở rộng thường trả về các giá trị boolean với:```c
Py_RETURN_TRUE;
Py_RETURN_FALSE;
```Các macro này trả về các tham chiếu thuộc sở hữu của các đối tượng đơn lẻ.
## 17.8 Kiểm tra giá trị thực
Các đối tượng số tham gia kiểm tra giá trị thật.```python
bool(0) # False
bool(1) # True
bool(-1) # True
bool(0.0) # False
bool(0j) # False
```Quy tắc cho các loại số rất đơn giản:```text
zero value
false
nonzero value
true
```Kiểm tra sự thật sử dụng máy móc giao thức loại. Ở cấp độ C, các kiểu số cung cấp hành vi thông qua các vị trí như chuyển đổi boolean.
## 17.9 Các phép toán số nguyên
Các phép toán số nguyên phổ biến bao gồm:```python
a + b
a - b
a * b
a // b
a % b
a ** b
a << n
a >> n
a & b
a | b
a ^ b
~a
```Những bản đồ này tới các khe số.
Về mặt khái niệm:```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
```Mỗi thao tác nhận các đối tượng Python và trả về một đối tượng Python.
Ngay cả khi kết quả khớp với từ máy, kết quả vẫn là đối tượng Python.
## 17.10 Phép chia số nguyên
Python có hai toán tử chia chính.```python
a / b # true division
a // b # floor division
```Đối với số nguyên:```python
print(5 / 2) # 2.5
print(5 // 2) # 2
/trả về một số float cho số nguyên thông thường.//trả về kết quả sàn.
Đối với các giá trị âm:python print(-5 // 2) # -3 print(-5 % 2) # 1 Danh tính giữ:text a == (a // b) * b + (a % b) với%mang theo quy ước về dấu hiệu theo yêu cầu của ngữ nghĩa Python.
Hoạt động bit 17.11
Các số nguyên hỗ trợ các phép toán bit như thể được biểu diễn dưới dạng phần bù hai với phần mở rộng dấu vô hạn.python x & y x | y x ^ y ~x x << n x >> n Ví dụ:python print(5 & 3) # 1 print(5 | 3) # 7 print(5 ^ 3) # 6 print(~5) # -6 Kết quả của~xsau:```text
~x == -x - 1
## 17.12 Phương thức số nguyên
Các phương thức số nguyên thể hiện các thuộc tính hữu ích của giá trị bên trong.```python
x = 1024
print(x.bit_length())
print(x.bit_count())
bit_lengthtrả về số bit cần thiết để biểu thị giá trị tuyệt đối.```python
print((0).bit_length()) # 0
print((1).bit_length()) # 1
print((2).bit_length()) # 2
print((3).bit_length()) # 2
`bit_count`trả về số lượng dân số của giá trị tuyệt đối.```python
print((7).bit_count()) # 3
```Các phương pháp này thường ánh xạ hiệu quả tới các hoạt động chữ số bên trong và nội tại CPU nếu có.
## 17.13 Chuyển đổi số nguyên thành byte
Các số nguyên có thể được chuyển đổi sang và từ các chuỗi byte.```python
x = 1024
data = x.to_bytes(2, byteorder="big")
print(data)
again = int.from_bytes(data, byteorder="big")
print(again)
```Điều này quan trọng đối với:```text
binary protocols
cryptography
file formats
network byte order
serialization
```Endianness phải rõ ràng.```python
x.to_bytes(4, "big")
x.to_bytes(4, "little")
```Cùng một số nguyên tạo ra các bố cục byte khác nhau tùy thuộc vào thứ tự byte.
## 17.14 Đối tượng nổi
Python`float`thường là một lớp bao quanh một C double.
Về mặt khái niệm:```text
PyFloatObject
PyObject header
double value
```Một đối tượng float có kích thước cố định.```python
x = 1.5
y = 2.25
print(x + y)
```Các hoạt động nổi được thực hiện thông qua các khe số và các hoạt động dấu phẩy động nền tảng.
Điểm mấu chốt: số float là số dấu phẩy động nhị phân gần đúng.
## 17.15 Dấu phẩy động nhị phân
Phân số thập phân thường không thể được biểu diễn chính xác dưới dạng nhị phân.```python
print(0.1 + 0.2)
```Điều này thường in:```text
0.30000000000000004
```Vấn đề là sự đại diện.`0.1`không có khai triển nhị phân hữu hạn, giống như`1/3`không có khai triển thập phân hữu hạn.
Vì vậy, giá trị được lưu trữ là số float nhị phân có thể biểu thị gần nhất.
Điều này ảnh hưởng đến sự bình đẳng:```python
print(0.1 + 0.2 == 0.3) # False
```Sử dụng so sánh dựa trên dung sai cho công việc số gần đúng:```python
import math
math.isclose(0.1 + 0.2, 0.3)
```## 17.16 Giá trị đặc biệt thả nổi
Phao bao gồm các giá trị đặc biệt:```python
inf = float("inf")
nan = float("nan")
neg_inf = float("-inf")
```Infinity hoạt động như mong đợi trong nhiều so sánh:```python
print(inf > 1e308) # True
```NaN là không bình thường:```python
nan = float("nan")
print(nan == nan) # False
print(nan != nan) # True
```NaN có nghĩa là “không phải số”. Nó không so sánh bằng chính nó.
Hành vi này tuân theo các quy tắc dấu phẩy động, không phải logic nhận dạng đối tượng thông thường.
## 17.17 Băm nổi và bình đẳng
Các loại số phối hợp bình đẳng và băm.```python
print(1 == 1.0) # True
print(hash(1) == hash(1.0))
```Nếu hai đối tượng số so sánh bằng nhau thì chúng phải có cùng hàm băm.
Điều này cho phép từ điển hoạt động chính xác:```python
d = {}
d[1] = "int"
d[1.0] = "float"
print(d)
```Nhiệm vụ thứ hai cập nhật vị trí quan trọng tương tự vì`1 == 1.0`.
Sự đẳng thức số kiểu chéo này rất thuận tiện, nhưng nó có thể gây ngạc nhiên cho những người dùng mong đợi các khóa phân biệt loại.
## 17.18 Chuyển đổi thả nổi
Chuyển đổi:```python
float(1)
int(1.9)
round(1.9)
intcắt ngắn về 0:```python
print(int(1.9)) # 1
print(int(-1.9)) # -1
`round`tuân theo quy tắc làm tròn của Python:```python
print(round(2.5))
print(round(3.5))
```Chuyển đổi dấu phẩy động sang số nguyên có thể làm mất thông tin nếu số float không thể hiện chính xác giá trị mong muốn.
Đối với số học tài chính thập phân, hãy sử dụng`decimal.Decimal`thay vì thả nổi nhị phân.
## 17.19 Đối tượng phức tạp
Python`complex`lưu trữ hai giá trị dấu phẩy động:```text
PyComplexObject
PyObject header
double real
double imag
```Ví dụ:```python
z = 3 + 4j
print(z.real)
print(z.imag)
```Phần thực và phần ảo là phần nổi.
Số phức hỗ trợ số học:```python
a = 1 + 2j
b = 3 + 4j
print(a + b)
print(a * b)
```Họ không hỗ trợ đặt hàng:```python
(1 + 2j) < (3 + 4j) # TypeError
```Không có thứ tự tổng tự nhiên cho các số phức trong mô hình số của Python.
## 17.20 Số học phức tạp
Phép nhân phức tạp sau:```text
(a + bi)(c + di) = (ac - bd) + (ad + bc)i
```Python xử lý việc này bên trong việc triển khai kiểu phức tạp.
Ví dụ:```python
a = 1 + 2j
b = 3 + 4j
print(a * b) # (-5+10j)
```Sự phân chia và quyền hạn cũng được hỗ trợ.
Đối với công việc tính toán nâng cao,`cmath`module cung cấp các hàm toán học nhận biết phức tạp.```python
import cmath
print(cmath.sqrt(-1))
```## 17.21 Tháp số
Mô hình số của Python có hệ thống phân cấp khái niệm:```text
numbers.Number
numbers.Complex
numbers.Real
numbers.Rational
numbers.Integral
```Các loại tích hợp gần như phù hợp như:
| Loại | Vai trò khái niệm |
| --------- | ----------------------------- |
|`complex`| Phức tạp |
|`float`| Thực tế |
|`int`| Tích phân |
|`bool`| Phân lớp tích phân trong thực tế |
các`numbers`module cung cấp các lớp cơ sở trừu tượng cho các giao thức số.
Hầu hết các hoạt động CPython thông thường sử dụng các khe kiểu cụ thể thay vì gửi lớp cơ sở trừu tượng, nhưng hệ thống phân cấp giúp xác định hành vi dự kiến.
## 17.22 Các phép toán hỗn hợp số
Các phép toán số hỗn hợp tuân theo các quy tắc ép buộc và gửi đi.```python
print(1 + 2.5) # 3.5
print(1 + 2j) # (1+2j)
print(1.5 + 2j) # (3.5+2j)
```Hướng mở rộng điển hình:```text
int -> float -> complex
```Đây là một mô hình khái niệm. Việc triển khai thực tế sử dụng công văn hoạt động nhị phân giữa các loại toán hạng.
Đối với các lớp số do người dùng định nghĩa, trả về`NotImplemented`cho phép toán hạng khác thử hành vi được phản ánh.```python
class N:
def __add__(self, other):
return NotImplemented
```## 17,23`__index__`
`__index__`có nghĩa là một đối tượng có thể được hiểu là một số nguyên chính xác để lập chỉ mục và cắt.```python
class Index:
def __index__(self):
return 2
xs = [10, 20, 30, 40]
print(xs[Index()])
```Điều này chặt chẽ hơn`__int__`.
Các trường hợp sử dụng bao gồm:```text
list indexes
slice bounds
range arguments
bit operations
low-level integer contexts
```Ở cấp độ C, điều này tương ứng với hành vi giao thức chỉ số nguyên.
## 17,24`__int__`, `__float__`, Và`__complex__`Các phương thức chuyển đổi cho phép các đối tượng giống số tùy chỉnh chuyển đổi thành các kiểu số có sẵn.```python
class Value:
def __int__(self):
return 10
def __float__(self):
return 10.5
def __complex__(self):
return 10.5 + 2j
```Cách sử dụng:```python
v = Value()
print(int(v))
print(float(v))
print(complex(v))
```Những chuyển đổi này tạo ra các đối tượng số tích hợp.
Chúng không tự động tạo kiểu tùy chỉnh đầy đủ bằng số. Các phương pháp số học vẫn cần được thực hiện riêng biệt.
## 17,25 Khe số
Hành vi số được thực hiện thông qua các vị trí.
Bảng slot khái niệm:```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
...
```Cú pháp Python ánh xạ vào bảng này.
| Cú pháp | Khái niệm máy đánh bạc |
| ---------- | --------------------- |
|`a + b`| cộng |
|`a - b`| phép trừ |
|`a * b`| phép nhân |
|`a @ b`| phép nhân ma trận |
|`-a`| phủ định |
|`abs(a)`| giá trị tuyệt đối |
|`bool(a)`| giá trị thật |
|`int(a)`| chuyển đổi số nguyên |
|`float(a)`| chuyển đổi thả nổi |
|`a << b`| dịch chuyển trái |
Bảng vị trí này là lý do tại sao các kiểu số mở rộng và tích hợp sẵn có thể tích hợp với cú pháp Python.
## 17.26 Các thao tác số tại chỗ
Các nhà khai thác tại chỗ bao gồm:```python
x += y
x -= y
x *= y
x //= y
x **= y
```Đối với các số không thể thay đổi, các thao tác tại chỗ sẽ tạo một đối tượng mới và gắn lại tên.```python
x = 10
before = id(x)
x += 1
after = id(x)
print(before == after) # usually False
```Đối với các đối tượng giống như số có thể thay đổi, một loại có thể thực hiện đột biến thực sự tại chỗ.
Tích hợp sẵn`int`, `float`, Và`complex`là bất biến.
## 17,27 Thập phân và phân số
Thư viện chuẩn cung cấp các kiểu số bên ngoài các phần dựng sẵn cốt lõi.`decimal.Decimal`cung cấp số học dấu phẩy động thập phân.```python
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))
fractions.Fractioncung cấp số học hợp lý.```python
from fractions import Fraction
print(Fraction(1, 3) + Fraction(1, 6))
Sử dụng chúng khi ngữ nghĩa của chúng phù hợp với vấn đề:
| Cần | Loại |
| ----------------------------------- | ---------- |
| Số nguyên tổng quát |`int`|
| Công trình số khoa học gần đúng |`float`|
| Số học phức tạp |`complex`|
| Số học tài chính thập phân |`Decimal`|
| Số học hợp lý chính xác |`Fraction`|
## 17.28 C API: Tạo số
Tạo một số nguyên:```c
PyObject *x = PyLong_FromLong(42);
if (x == NULL) {
return NULL;
}
```Tạo từ loại C rộng hơn:```c
PyObject *x = PyLong_FromLongLong(value);
```Tạo phao:```c
PyObject *f = PyFloat_FromDouble(3.14);
if (f == NULL) {
return NULL;
}
```Tạo phức chất:```c
PyObject *z = PyComplex_FromDoubles(1.0, 2.0);
if (z == NULL) {
return NULL;
}
```Mỗi trả về một tài liệu tham khảo mới. Người gọi sở hữu nó.
## 17.29 C API: Trích xuất số
Trích xuất một C dài:```c
long value = PyLong_AsLong(obj);
if (value == -1 && PyErr_Occurred()) {
return NULL;
}
```Việc kiểm tra lỗi quan trọng vì`-1`có thể là một kết quả hợp lệ.
Trích xuất một double:```c
double value = PyFloat_AsDouble(obj);
if (value == -1.0 && PyErr_Occurred()) {
return NULL;
}
```Một lần nữa, hãy kiểm tra trạng thái ngoại lệ.
Đối với các ngữ cảnh giống như số nguyên, hãy sử dụng API chuyển đổi chỉ mục khi yêu cầu ngữ nghĩa số nguyên chính xác.
## 17.30 Tràn ranh giới C
Số nguyên Python có độ chính xác tùy ý. Số nguyên C có chiều rộng cố định.
Chuyển đổi này có thể thất bại:```c
long value = PyLong_AsLong(obj);
```nếu như`obj`quá lớn đối với C`long`.
Ví dụ cấp độ Python:```python
x = 10 ** 100
```Giá trị này hợp lệ dưới dạng Python`int`, nhưng có thể không vừa với bất kỳ loại số nguyên C nào.
Mã mở rộng C phải xử lý lỗi tràn.
Một lỗi phổ biến là giả sử Python`int`luôn phù hợp với`int`, `long`, hoặc`size_t`.
## 17.31 Ghi chú về Hiệu suất
Hiệu suất số phụ thuộc vào đại diện.
Số học số nguyên nhỏ được tối ưu hóa nhưng vẫn hoạt động trên các đối tượng Python.
Một vòng lặp chặt chẽ:```python
total = 0
for i in range(10_000_000):
total += i
```thực hiện nhiều thao tác cấp Python:```text
load total
load i
integer addition
create or retrieve result integer
store total
loop control
```Tốc độ này chậm hơn nhiều so với C tương đương trên số nguyên máy thô.
Đối với các mảng số lớn, hãy sử dụng các thư viện chuyên biệt để lưu trữ các giá trị thô một cách gọn gàng, chẳng hạn như`array`, `memoryview`hoặc NumPy.
Các kiểu số tích hợp của Python tối ưu hóa ngữ nghĩa vô hướng, chứ không phải tính toán vectơ số dày đặc.
## 17.32 Ghi chú bộ nhớ
Danh sách các số nguyên lưu trữ các tham chiếu đến các đối tượng số nguyên.```python
xs = [1, 2, 3, 4]
```Về mặt khái niệm:```text
list
[ptr][ptr][ptr][ptr]
| | | |
v v v v
int int int int
```Một mảng lưu trữ các giá trị thô:```python
from array import array
xs = array("i", [1, 2, 3, 4])
```Về mặt khái niệm:```text
array
[int][int][int][int]
```Đây là lý do tại sao mảng tiết kiệm bộ nhớ hơn đối với dữ liệu số lớn đồng nhất.
## 17.33 Những cạm bẫy số thường gặp
| Cạm bẫy | Ví dụ | Cách tiếp cận tốt hơn |
| -------------------------------------- | ------------------------------- | ----------------------------- |
| sử dụng`is`cho các con số |`x is 1000` | `x == 1000`|
| Mong đợi độ chính xác thập phân từ float |`0.1 + 0.2 == 0.3` | `math.isclose`hoặc`Decimal`|
| kiên trì`hash()` | `hash(x)`như ID ổn định |`hashlib`|
| Giả sử Python int phù hợp với C long |`PyLong_AsLong`bỏ chọn | Kiểm tra tràn |
| Sử dụng danh sách cho mảng số dày đặc |`[0] * n`cho dữ liệu số khổng lồ |`array`hoặc NumPy |
| So sánh NaN thông thường |`nan == nan`| Sử dụng`math.isnan`|
| Bỏ qua chi phí tăng trưởng số nguyên | sức mạnh to lớn trong con đường nóng bỏng | Xem xét giới hạn thuật toán |
## 17.34 Mô hình tinh thần
Sử dụng mô hình này:```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
```Ở cấp độ CPython:```text
numeric operation
dispatch through type slots
operate on concrete numeric representation
allocate or return result object
manage references
```##17.35 Tóm tắt
CPython triển khai số nguyên, số float và số phức dưới dạng đối tượng Python có bố cục C chuyên dụng.`int`sử dụng bộ lưu trữ có độ chính xác tùy ý, do đó, nó tránh được tình trạng tràn chiều rộng cố định nhưng phải trả chi phí tăng theo kích thước giá trị.`float`bao bọc một C double và kế thừa hành vi dấu phẩy động nhị phân.`complex`lưu trữ hai số nhân đôi và hỗ trợ số học phức tạp.
Những loại số này là bất biến. Các thao tác tạo đối tượng kết quả thay vì thay đổi toán hạng. Hành vi của chúng được tích hợp thông qua các khe số, giao thức chuyển đổi, quy tắc băm và chức năng API C.