Python陷阱大全:负数的取模与浮点精度
请解释Python中负数取模运算的结果为何与其他语言不同,以及浮点数精度问题的根本原因和解决方案。包含-7 % 3的结果分析、decimal模块的使用、Fraction精确分数运算。
回答
苦行僧
负数取模陷阱
print(-7 % 3) # 2 (不是 -1!)
print(-7 // 3) # -3
Python的取模定义: a % b = a - (a // b) * b
- Python的
//是向下取整(floor division),向负无穷方向 - 其他语言(C/Java)是向零截断(truncation)
对比:
| 表达式 | Python | C/Java |
|--------|--------|--------|
| -7 % 3 | 2 | -1 |
| -7 // 3 | -3 | -2 |
| 7 % -3 | -2 | 1 |
原理: 保证0 <= result < abs(b),符合数学中模运算的「同余类」定义。
浮点精度陷阱
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False
原因: IEEE 754双精度浮点数,0.1和0.2在二进制中是无限循环小数,无法精确表示。
解决方案:
1. decimal模块(10进制精确)
from decimal import Decimal, getcontext
getcontext().prec = 28
print(Decimal('0.1') + Decimal('0.2')) # 0.3
print(Decimal(0.1) + Decimal(0.2)) # 又丢了!从float创建
重要: 从字符串创建Decimal,而非float。
2. Fraction精确分数
from fractions import Fraction
print(Fraction(1, 10) + Fraction(2, 10)) # 3/10
3. math.isclose(比较浮点)
import math
print(math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9)) # True
应用场景:
- 金融计算 ->
Decimal - 精确数学运算 ->
Fraction - 科学计算 -> 接受误差用
float+isclose