为什么 0.1 + 0.2 不等于 0.3?聊聊 Python 里被低估的 float
先抛结论:Python 里的 float 不是"小数",而是"浮点数"——这两个词在数学上看起来差不多,在计算机里却是两个物种。
如果你把 float 当成数学意义上的小数来用,迟早会在某一行 print 里看到一个诡异的 0.30000000000000004,然后开始怀疑人生。这不是 bug,这是二进制世界给十进制人类开的一个玩笑。
一道题先看看你的直觉对不对
有这么一道题:
以下哪个选项是 Python 中合法的 float 类型值?
A. 123 B. "3.14" C. 0.5 D. True
答案是 C。看起来简单,但这道题真正在筛选的是:你能不能一眼区分"数字的外形"和"数字的类型"。
123是int,不是 float。虽然123 + 0.5会自动变成 float,但123本身不是。"3.14"是str。带引号就是文本,哪怕里面全是数字字符。True是bool,本质是int的子类,跟 float 没关系。0.5有小数点,Python 直接识别为 float。
这里有一个很多人会忽略的细节:判断类型看的是写法,不是数值。 2.0 在数学上等于 2,但 type(2.0) 返回的是 <class 'float'>,不是 <class 'int'>。小数点一旦出现,类型就变了。
print(type(2)) # <class 'int'>
print(type(2.0)) # <class 'float'>
就这一条,每年都能坑到一批刚接触 Python 的人。他们看到 2.0 觉得"这不就是 2 吗",然后在类型判断上翻车。
float 的运算规则:一个"传染"逻辑
float 的运算有一个非常一致的原则:只要参与运算的任意一个数是 float,结果就是 float。
10.5 // 3.0 # 结果是 3.0,不是 3
注意整除 //。很多人以为整除一定返回整数,但当操作数里有 float 时,结果仍然是 float——只是小数部分被截掉了。3.0 和 3 在打印时看起来差不多,但类型完全不同。
这个规则的意义在于:Python 在做类型推断时,会选一个"能容纳所有参与者的最宽类型"。整数和浮点数混合运算时,整数会被自动"提升"为浮点数,然后按浮点规则计算。
理解了这一点,你就不会对下面这行感到意外:
total = 15.5 * 30 # 465.0,不是 465
精度问题:不是 Python 的锅
0.1 + 0.2 输出 0.30000000000000004,这件事经常被拿来当"Python 不精确"的证据。但说实话,这跟 Python 没关系,任何用 IEEE 754 浮点数标准的语言都一样。
原因很简单:十进制里的 0.1,在二进制里是一个无限循环小数。就像十进制写不出 1/3 的精确值一样,二进制也写不出 0.1 的精确值。计算机只能存一个近似值,误差在运算中积累,就变成了那个尾巴。
所以有一条铁律:永远不要用 == 比较两个 float。
# 错误做法
if a == b: ...
# 正确做法
if abs(a - b) < 1e-9: ...
那什么时候需要精确计算?涉及钱的场景。Python 提供了 decimal 模块,可以精确表示十进制小数。不过这是进阶内容,日常做练习、写小工具,float 的精度完全够用。
类型转换:int() 的"砍"和 round() 的"舍"
整数和小数之间的转换是高频操作,但这里有一个容易忽视的细节:
int(9.99) # 9,不是 10
int(-2.7) # -2,不是 -3
int() 对 float 的处理是向零取整——正数砍掉小数部分,负数也砍掉小数部分(所以 -2.7 变成 -2,而不是 -3)。它不是四舍五入,也不是向下取整,是"朝零方向靠拢"。
如果你想要四舍五入,得用 round():
round(3.1415) # 3
round(3.1415, 2) # 3.14
这两个函数的语义完全不同,用错了在边界情况上会出问题。比如做分页计算时,int() 和 round() 选哪个,直接决定最后一页是满的还是空的。
回到那道类型判断题
现在再看 print(type(2.0)) 输出什么,你应该不会犹豫了——<class 'float'>。
这道题表面上考的是 type() 函数的用法,实际上考的是:你有没有建立起"类型由写法决定"这个认知。
2.0有小数点 → float2没有小数点 → int"2.0"有引号 → strTrue是关键字 → bool
Python 中没有 number 这个内置类型,只有 int、float、complex 这些具体类型。选项里出现 number 基本可以直接排除。
写在最后
float 这个类型,入门时觉得简单——不就是小数吗?用着用着才发现,它的运算规则、精度陷阱、类型转换细节,每一条都值得单独拿出来想清楚。
如果你已经掌握了 float 的基本用法,接下来可以往这几个方向走:
decimal模块——理解为什么金融计算不能用 float- 格式化输出——
f"{price:.2f}"这种写法在实际项目中比round()更常用 type()和isinstance()——学会在运行时判断类型,而不是靠肉眼猜
类型系统是 Python 的地基之一。把 int 和 float 的边界搞清楚,后面学其他类型时会轻松很多。
关于作者
我是赵老师,持有 NOI 信息学奥赛教练证书,拥有 15 年以上的软件开发经验,从事信息学少儿编程教学已有 8 年时间。
这些年累计帮助 多名 学生通过编程特长升入自己心仪的目标学校。
如果你在编程学习上有任何疑问,欢迎联系我:18620372957(微信同号)