Python 中 int、float、Decimal 有什么区别?
简化版
int 表示整数,Python 里的整数支持任意精度;float 是双精度浮点数,适合科学计算但存在二进制精度误差;Decimal 是十进制小数,适合金额、财务等需要可控精度的场景。
详细版
例子:
print(0.1 + 0.2) # 0.30000000000000004
这不是 Python 算错了,而是二进制浮点数无法精确表示很多十进制小数。
金额场景更推荐:
from decimal import Decimal
price = Decimal("0.1")
tax = Decimal("0.2")
print(price + tax) # 0.3
区别:
| 类型 | 特点 | 适合场景 |
|---|---|---|
int | 任意精度整数 | 计数、ID、位运算、精确整数 |
float | 二进制浮点数,速度快但有误差 | 科学计算、近似计算 |
Decimal | 十进制精确计算,可控制舍入 | 金额、财务、精度敏感 |
注意:创建 Decimal 时尽量从字符串创建,不要从 float 创建:
Decimal("0.1") # 推荐
Decimal(0.1) # 会带入 float 误差
完整版教学
一、先按“精确性”和“表示方式”分
int、float、Decimal 的区别不是“谁高级”,而是解决不同问题。int 表示精确整数,Python 会自动扩展位数;float 表示二进制浮点近似值,速度快、生态广,但不能精确表示很多十进制小数;Decimal 表示十进制小数,能控制精度和舍入,更适合金额和财务规则。
| 类型 | 表示方式 | 精确性 | 典型场景 |
|---|---|---|---|
int | 任意精度整数 | 整数精确 | 计数、ID、位运算 |
float | IEEE 754 双精度二进制浮点 | 近似 | 科学计算、机器学习、指标 |
Decimal | 十进制小数 | 可控十进制精度 | 金额、税率、财务结算 |
面试时不要简单说“float 不精确,Decimal 精确”就结束。更准确是:float 对二进制可表示的数可以精确,对很多十进制小数只能近似;Decimal 能按十进制语义表示和舍入,但仍受上下文精度和业务量化规则约束。
二、Python int 为什么说是任意精度
在很多语言里,整数有固定范围,例如 32 位或 64 位。Python 的 int 会根据数值大小自动扩展精度,只受可用内存限制。
n = 10 ** 100
print(n)
这对开发很友好,不容易整数溢出。但代价是超大整数运算比固定机器整数更重。
常见用途:
- 计数;
- 下标;
- ID;
- 时间戳;
- 位运算;
- 精确整数计算。
如果用 64 位有符号整数,最大值约是 9.22e18;而 Python 的 10 ** 100 远远超过这个范围仍能正确表示。代价是大整数越大,占用的内存越多,运算也越慢。比如处理密码学大数、超大阶乘时,Python 写起来简单,但不代表没有性能成本。
三、float 的误差从哪里来
float 通常使用 IEEE 754 双精度二进制浮点数。问题在于,很多十进制小数无法用有限二进制精确表示。
就像十进制无法精确表示 1/3:
0.333333...
二进制也无法精确表示很多十进制小数,例如 0.1。
所以:
0.1 + 0.2 == 0.3
结果是:
False
比较 float 时应该使用容差:
import math
math.isclose(0.1 + 0.2, 0.3)
可以用公式理解浮点比较:
不要直接判断 a == b
应该判断 abs(a - b) <= 可接受误差
math.isclose 默认会结合相对误差和绝对误差,比手写简单容差更稳。面试里重点不是背 IEEE 754 的全部细节,而是能说出二进制表示导致十进制小数近似、比较要用容差、金额不要用 float 做核心计算。
易错点:
0.1 + 0.2的显示结果不是 Python 算错,而是二进制浮点近似值转换回十进制展示时暴露了误差。
四、Decimal 为什么适合金额
Decimal 按十进制语义处理小数,并且可以配置精度和舍入规则。
from decimal import Decimal, getcontext
getcontext().prec = 10
amount = Decimal("19.99")
count = Decimal("3")
print(amount * count)
金额计算关心的是十进制下的精确表达和舍入规则,所以不应该用 float 承担核心计算。
尤其不要这样:
Decimal(0.1)
因为 0.1 在传给 Decimal 前已经是带误差的 float。
正确创建方式通常是字符串或整数:
from decimal import Decimal, ROUND_HALF_UP
amount = Decimal("19.995")
print(amount.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)) # 20.00
quantize 很重要,因为财务金额不仅关心“算得精确”,还关心保留几位小数和如何舍入。不同业务可能要求四舍五入、银行家舍入、向上取整等规则,Decimal 给你的是可控能力,不是自动替你决定业务规则。
五、什么时候可以放心用 float
float 并不是坏东西,它适合大量近似数值计算:
- 科学计算;
- 机器学习;
- 图形计算;
- 统计估算;
- 对微小误差不敏感的指标。
这些场景通常更关心性能和数值方法,而不是每一位十进制小数完全符合财务规则。
机器学习和科学计算大量使用 float,是因为它速度快、硬件支持好、内存占用可控。比如 100 万个浮点特征用双精度大约 8MB 原始数值空间;如果每个都用 Decimal,对象开销和运算成本会高很多。选择类型要看误差是否可接受,而不是一看到小数就用 Decimal。
六、类型转换和混用边界
不要随意混用 float 和 Decimal。Decimal("0.1") 表示精确十进制 0.1;Decimal(0.1) 会把 float 已经存在的二进制误差带进来。Python 也不允许 Decimal 和 float 直接做某些算术混合,这是为了逼你明确语义。
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2")) # 0.3
print(Decimal(0.1)) # 0.100000000000000005551...
整数和 Decimal 混用通常更安全,因为整数本身精确。但只要进入 float,十进制精确语义就可能丢失。涉及金额的接口入参最好用字符串、整数分单位(如分)或 Decimal,而不是 float。
七、常见误区与追问
- 误区:Python 的
int会像 Javaint一样固定 32 位溢出。 Pythonint会自动扩展精度,不固定 32 位,但超大整数有内存和性能成本。 - 误区:
float是错误类型,不能用。 float 适合近似数值计算,问题是不要把它用于必须十进制精确的金额结算。 - 误区:
Decimal(0.1)就能消除 0.1 的误差。 传入 Decimal 前 0.1 已经是 float 近似值,应该用Decimal("0.1")。 - 追问:为什么
0.1 + 0.2 != 0.3? 很多十进制小数无法用有限二进制浮点精确表示,计算结果只是近似。 - 追问:float 比较应该怎么做? 用
math.isclose或业务容差,而不是直接==比较计算结果。 - 追问:金额为什么还要考虑舍入规则? Decimal 能精确表示十进制数,但保留几位、如何进位是业务规则,需要
quantize等方式明确。
八、加强记忆
把这题记成“三种数字三种承诺”:int 承诺整数精确且自动扩展,float 承诺高效近似和硬件友好,Decimal 承诺十进制语义和可控舍入。计数、ID、位运算用 int;科学计算、机器学习、指标估算用 float;金额、税率、财务结算用 Decimal 或整数分单位。回答时一定带上 0.1 + 0.2、math.isclose、Decimal("0.1") 这三个关键点。