在 Python 中,decimal 通常指的是 decimal 模块,而不是一个单独的函数,这个模块提供了对十进制浮点数的精确计算,非常适合用于金融、会计、科学计算等需要高精度和避免浮点数舍入误差的场景。

它的核心是 Decimal 类,你通过调用 decimal.Decimal() 来创建一个 Decimal 对象,这看起来就像一个函数,所以我们常称之为 decimal 函数。
为什么需要 decimal 模块?(问题的根源)
我们需要理解 Python 默认的浮点数(float)为什么会有问题,计算机使用二进制(0 和 1)来存储所有数据,包括小数。
很多十进制小数在二进制中是无限循环小数,就像 1/3 在十进制中是 0.333... 一样,计算机只能存储其近似值,这导致了精度损失。
示例:经典的浮点数陷阱

# 默认的 float 计算 a = 0.1 b = 0.2 print(a + b) # 输出: 0.30000000000000004 # 这显然不是我们期望的 0.3
这种微小的误差在大多数科学计算中可以忽略,但在金融计算中,0.00000000000004 的误差累积起来会导致严重的问题。
decimal 模块就是为了解决这个问题而生的,它使用我们熟悉的十进制进行计算,就像我们用笔和纸计算一样,可以精确表示像 0.1, 0.2 这样的数字。
如何使用 decimal 模块?
使用 decimal 模块通常分为三步:
- 导入模块:
import decimal - 设置精度(可选但推荐):根据你的需求设置全局精度。
- 创建 Decimal 对象:使用
decimal.Decimal()将整数、字符串或其他数字转换为Decimal类型。
基本用法示例
import decimal
# 1. 从字符串创建 Decimal (推荐方式,最精确)
# 注意:最好从字符串创建,而不是从 float 创建,因为 float 本身已经有误差了
d1 = decimal.Decimal('0.1')
d2 = decimal.Decimal('0.2')
print(f"d1: {d1}, type: {type(d1)}")
print(f"d2: {d2}, type: {type(d2)}")
# 2. 进行精确计算
result = d1 + d2
print(f"精确计算结果: {result}") # 输出: 0.3
# 3. 与 float 进行对比
f1 = 0.1
f2 = 0.2
print(f"float 计算结果: {f1 + f2}") # 输出: 0.30000000000000004
输出:
d1: 0.1, type: <class 'decimal.Decimal'>
d2: 0.2, type: <class 'decimal.Decimal'>
精确计算结果: 0.3
float 计算结果: 0.30000000000000004
核心功能详解
A. 精度控制
decimal 模块可以让你控制计算的精度(有效数字的个数)和舍入方式。
设置全局精度
import decimal
# 设置全局精度为 5 位有效数字
decimal.getcontext().prec = 5
# 进行计算
x = decimal.Decimal('1.23456789')
y = decimal.Decimal('9.87654321')
print(x * y) # 输出: 12.188 (只保留了5位有效数字)
常用舍入模式
decimal 模块提供了多种舍入模式,可以通过 decimal.getcontext().rounding 来设置。
decimal.ROUND_UP: 远离零舍入 (1.234 -> 1.24)decimal.ROUND_DOWN: 向零舍入 (1.234 -> 1.23)decimal.ROUND_HALF_UP: 四舍五入 (1.235 -> 1.24)decimal.ROUND_HALF_EVEN: 银行家舍入法(默认),舍入到最接近的偶数 (1.235 -> 1.24, 1.225 -> 1.22)
import decimal
# 设置舍入模式为四舍五入
decimal.getcontext().rounding = decimal.ROUND_HALF_UP
num = decimal.Decimal('2.675')
print(f"默认银行家舍入法: {num.quantize(decimal.Decimal('0.01'))}") # 2.67
decimal.getcontext().rounding = decimal.ROUND_HALF_UP
num = decimal.Decimal('2.675')
print(f"四舍五入模式: {num.quantize(decimal.Decimal('0.01'))}") # 2.68
B. 上下文管理
如果你只想在代码的某一部分使用高精度,而不影响全局,可以使用 localcontext() 上下文管理器。
import decimal
# 默认精度
print(f"默认精度: {decimal.getcontext().prec}") # 28
# 在一个代码块中临时改变精度
with decimal.localcontext() as ctx:
ctx.prec = 3 # 临时设置为 3 位精度
print(f"块内精度: {decimal.getcontext().prec}") # 3
x = decimal.Decimal('123.456')
y = decimal.Decimal('78.901')
print(f"块内计算结果: {x * y}") # 输出: 9740 (123 * 78 = 9594, 但实际计算是 9739.7..., 精度截断为 9740)
# 离开 with 块后,精度恢复默认
print(f"块外精度: {decimal.getcontext().prec}") # 28
C. 常用运算和函数
Decimal 对象支持所有标准的数学运算(, , , , 等),并且还有一些专用的方法。
import decimal
d = decimal.Decimal('10.5')
# 算术运算
print(d + 5) # 15.5
print(d * 2) # 21.0
print(d ** 2) # 110.25
# 比较运算
print(d > 5) # True
# 专用方法
print(d.sqrt()) # 平方根,输出 3.2403703492039303
print(d.to_integral_value()) # 转换为整数,输出 10
print(d.quantize(decimal.Decimal('0.00'))) # 四舍五入到小数点后两位,输出 10.50
重要注意事项
-
从字符串创建:为了获得最佳精度,始终使用字符串(
'0.1')来创建Decimal对象,如果从float创建(Decimal(0.1)),你实际上是将一个不精确的二进制浮点数转换为了Decimal,精度问题依然存在。# 错误的方式 d_bad = decimal.Decimal(0.1) d_good = decimal.Decimal('0.1') print(d_bad) # 输出: 0.1000000000000000055511151231257827021181583404541015625 print(d_good) # 输出: 0.1 -
混合类型运算:不能将
Decimal对象直接与int或float进行运算,你必须先将int或float转换为Decimal。d = decimal.Decimal('10.5') num_int = 5 num_float = 2.5 # 正确的做法 result1 = d + decimal.Decimal(str(num_int)) print(result1) # 15.5 result2 = d * decimal.Decimal(str(num_float)) print(result2) # 26.25 # 错误的做法 (会抛出 TypeError) # d + num_int # d * num_float
decimal vs. float:总结对比
| 特性 | decimal.Decimal |
float |
|---|---|---|
| 精度 | 可配置的、高精度(用户定义) | 固定的、有限精度(硬件决定,约15-17位有效数字) |
| 基数 | 十进制 | 二进制 |
| 用途 | 金融、会计、货币计算、需要精确小数的场景 | 科学计算、图形学、通用计算 |
| 性能 | 较慢(软件实现) | 非常快(硬件指令支持) |
| 创建方式 | decimal.Decimal('0.1') (推荐) |
1 或 float('0.1') |
| 内存占用 | 较大 | 较小 |
何时使用 decimal?
- 当你处理的数字是货币(如人民币、美元)时。
- 当计算结果对精度要求极高,不能有任何舍入误差时。
- 当你需要控制舍入行为(如银行家舍入法)时。
何时使用 float?
- 进行科学计算、工程计算或机器学习时。
- 当性能是首要考虑因素,且微小的精度误差可以接受时。
- 当处理的数据范围非常大或非常小时(
float可以表示极大或极小的数)。
