CC++ & Algorithm

为什么 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 有小数点 → float
  • 2 没有小数点 → int
  • "2.0" 有引号 → str
  • True 是关键字 → bool

Python 中没有 number 这个内置类型,只有 int、float、complex 这些具体类型。选项里出现 number 基本可以直接排除。

写在最后

float 这个类型,入门时觉得简单——不就是小数吗?用着用着才发现,它的运算规则、精度陷阱、类型转换细节,每一条都值得单独拿出来想清楚。

如果你已经掌握了 float 的基本用法,接下来可以往这几个方向走:

  1. decimal 模块——理解为什么金融计算不能用 float
  2. 格式化输出——f"{price:.2f}" 这种写法在实际项目中比 round() 更常用
  3. type() 和 isinstance()——学会在运行时判断类型,而不是靠肉眼猜

类型系统是 Python 的地基之一。把 int 和 float 的边界搞清楚,后面学其他类型时会轻松很多。


关于作者

我是赵老师,持有 NOI 信息学奥赛教练证书,拥有 15 年以上的软件开发经验,从事信息学少儿编程教学已有 8 年时间。

这些年累计帮助 多名 学生通过编程特长升入自己心仪的目标学校。

如果你在编程学习上有任何疑问,欢迎联系我:18620372957(微信同号)

这篇文章对你有帮助吗?

成为第一个评价的人

评论0

还没有评论,来抢沙发~

评论加载中...

想系统学习这个知识点?查看完整知识点 →