前言

在 Python 编程中,数字是我们最打交道的数据类型。其中,整型 (int)浮点型 (float) 是最基础、最常用的两种。看似简单,但如果不了解底层原理,特别是浮点数的精度问题,很容易在计算金额或科学运算时踩进“深坑”。

整型 (int)

Python 中的整型用于表示整数(没有小数部分的数字),包括正整数、负整数和零。

1. 核心特性:无限精度

与 C/Java 等语言不同,Python 3 的 int 类型没有固定的大小限制(不再区分 intlong)。只要你的内存够大,Python 就能处理任意大的整数。这使得 Python 非常适合进行大数运算。

2. 基础用法与进制表示

除了十进制,Python 还支持二进制、八进制和十六进制的直接输入。

# 1. 基础赋值
a = 10
b = -5
c = 0

# 2. 大数运算(自动处理,不会溢出)
big_num = 2 ** 100
print(f"2的100次方: {big_num}")
# 输出: 1267650600228229401496703205376

# 3. 多进制表示
n_bin = 0b1010  # 二进制 (0b 开头) -> 10
n_oct = 0o12    # 八进制 (0o 开头) -> 10
n_hex = 0xA     # 十六进制 (0x 开头) -> 10

print(f"二进制: {n_bin}, 八进制: {n_oct}, 十六进制: {n_hex}")

浮点型 (float)

浮点型用于表示带小数点的数字,或者使用科学计数法表示的数字。

基础用法

Python 的 float 类型通常对应于 C 语言中的 double(双精度浮点数),遵循 IEEE 754 标准。

# 1. 小数形式
pi = 3.14159
negative = -0.01

# 2. 科学计数法 (e 代表 10 的幂)
large_f = 1.23e9       # 1.23 * 10^9
small_f = 1.5e-3       # 1.5 * 10^-3 (0.0015)

print(f"科学计数法: {large_f}")
print(type(pi)) # <class 'float'>

高能预警:浮点数的精度问题

新手在 Python 中最容易遇到的“灵异事件”莫过于此:

print(0.1 + 0.2)
# 你以为会输出: 0.3
# 实际输出: 0.30000000000000004

为什么会这样?

这不是 Python 的 bug,而是计算机底层二进制存储机制决定的。

  • 十进制中,我们用 $1/10$ 表示 0.1。
  • 二进制中,计算机需要用 $1/2, 1/4, 1/8...$ 来拼凑数字。
  • 遗憾的是,0.1 在二进制中是一个无限循环小数 (0.0001100110011...)。

由于计算机内存(64位)是有限的,它不得不截断这个无限循环小数。截断就意味着精度的丢失。当你把两个本身就存在微小误差的数相加时,误差被放大,最终显示出来。

解决精度问题的 4 种方案

在涉及金融计算科学实验等对精度要求极高的场景下,直接使用 float 是不安全的。以下是常见的解决方案:

方案 1:用于展示 —— formatround (不推荐用于计算)

如果你只是想把结果打印得好看一点,可以使用格式化字符串或 round()

  • 注意round() 在 Python 3 中采用“银行家舍入法”(四舍六入五取偶),且 round() 本身仍返回 float,可能依然带有微小误差。
a = 0.1 + 0.2

# 方法 A: 字符串格式化 (推荐用于展示)
print(f"{a:.2f}")  # 输出: 0.30

# 方法 B: round 函数
print(round(a, 2)) # 输出: 0.3

round函数还是比较简单的,函数第二个参数表示保留几位小数。

f这个字符串格式化稍微复杂一点,它的语法结构如下:

f"{value:[width][.precision][type]}"
  • value:要格式化的数值
  • width:最小总宽度(可省略)
  • .precision:小数点后位数(对 f 类型有效)
  • type:常用有 f(固定小数)、e(科学计数)、g(自动选择)
💡 示例:{a:.2f} 表示“以固定小数形式显示,保留2位小数,不足位数会自动补0”。

方案 2:用于比较 —— math.isclose

永远不要使用 == 来判断两个浮点数是否相等。应该判断它们是否“足够接近”。

import math

a = 0.1 + 0.2
b = 0.3

# 错误做法
if a == b:
    print("相等")
else:
    print("不相等") # 这里会被执行

# 正确做法:使用 math.isclose
# rel_tol 是相对误差,abs_tol 是绝对误差
if math.isclose(a, b, rel_tol=1e-9):
    print("数学上相等") # 这里会被执行

为什么rel_tol是1e-9?

rel_tol=1e-9(即 0.000000001)并不是随意选的,而是基于浮点数精度的工程经验和IEEE 754 双精度浮点数(float64)的特性得出的一个合理默认值。NumPy、SciPy 等科学计算库也常用 1e-91e-8 作为默认容差。Python 官方文档明确说明:1e-9 是“适用于大多数情况的合理默认值”。

什么时候使用abs_tol?

abs_tol 适用于你关心的数值范围非常小(接近零)的情况。比如:比较两个接近0的数。

import math

a = 1e-12
b = 0.0

# 仅用 rel_tol 会失败(因为 max(|a|,|b|) ≈ 0,rel_tol * 0 = 0)
print(math.isclose(a, b, rel_tol=1e-9))        # False ❌

# 必须使用 abs_tol
print(math.isclose(a, b, abs_tol=1e-11))       # True ✅

新手也不用想那么多,以后遇到了自然再了解即可。

方案 3:金融级精确计算 —— decimal 模块 (推荐)

Python 标准库提供了 decimal.Decimal 类,用于十进制的精确运算。这是解决金额计算问题的标准答案。

关键点:创建 Decimal 对象时,必须传入字符串!

from decimal import Decimal

# 错误示范:传入 float,精度已经丢了
d_wrong = Decimal(0.1)
print(f"错误创建: {d_wrong}")
# 输出: 0.100000000000000005551115123125...

# 正确示范:传入字符串
d1 = Decimal('0.1')
d2 = Decimal('0.2')
d3 = d1 + d2

print(f"正确结果: {d3}")       # 输出: 0.3
print(f"类型: {type(d3)}")    # <class 'decimal.Decimal'>

# Decimal 可以方便地进行四舍五入
# 设置精度保留2位小数,ROUND_HALF_UP 是标准的四舍五入
from decimal import ROUND_HALF_UP
price = Decimal('1.125')
print(price.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)) # 1.13

方案 4:分数运算 —— fractions 模块

如果你需要处理 $1/3$ 这种在十进制和二进制都是无限循环的数,可以使用分数模块。

from fractions import Fraction

f1 = Fraction(1, 3)  # 1/3
f2 = Fraction(1, 6)  # 1/6

result = f1 + f2
print(f"分数计算结果: {result}") # 输出: 1/2
print(float(result))            # 输出: 0.5

总结

  1. 整型 (int):在 Python 3 中拥有无限精度,大胆使用,不用担心溢出。
  2. 浮点型 (float):基于 IEEE 754 标准,运算速度快,但存在二进制存储带来的精度误差
  3. 避坑指南

    • 比较:永远不要用 == 判断浮点数,请使用 math.isclose()
    • 计算:涉及到钱或高精度需求,请务必使用 decimal 模块,并且记得传字符串给 Decimal。
分类: Python 新手教程 标签: Python整型int浮点型float精度Decimal

评论

暂无评论数据

暂无评论数据

目录