不同类型的数字要一起运算就得【换户口】:自动转换由编译器悄悄办(小类型升大类型,安全)、强制转换是你亲手办(可能截断丢数据);而溢出是换不过来的【爆仓】——21 亿加 1 变成负 21 亿是本节的招牌事故。
3-7 讲了整数与浮点两大阵营的范围,这一篇讲【跨越阵营时发生什么】。溢出是 3-7 【范围封顶】的直接后果,也是 C++ 课 2.4.1 类型系统的另一面。
一、自动转换(隐式):编译器只办【升级】
运算里两种类型相遇时,编译器自动把【小】的转成【大】的——升级规则:整数比字节数,浮点压过整数:
int + float -> float (int 升级为 float 再算)
int8 + int32 -> int32
print(3 + 0.5) # 3.5 —— Python 自动把 3 升级为浮点
print(True + 1) # 2 —— True 被当 1(布尔是整数的特例)
为什么只【升】不【降】:int 转 float 可能丢精度、float 转 int 要丢小数——自动转换只做不丢信息的那一档,其余要你亲自动手并负责。
二、强制转换(显式):你签字,你负责
print(int(3.7)) # 3 —— 直接砍掉小数(不是四舍五入!)
print(int(3.2)) # 3
print(float(3)) # 3.0
print(str(255)) # "255" —— 数字变字符串
print(int("255")) # 255 —— 字符串变数字(回收树莓派课 6-3:input() 永远返回字符串,接数值必须转)
int() 截断方向要记牢:int(3.7) 得 3、int(-3.7) 得 -3——一律朝零砍,不是四舍五入(四舍五入要用 round)。
三、溢出:盒子装不下的爆仓瞬间
整数类型范围封顶(3-7 的表),超出就是溢出。C 家族的行为:
int32 最大 2147483647(2^31 - 1)+ 1 -> -2147483648
加 1 反而变最大负数——二进制只有 31 位表示大小,装满后进位【卷】进了符号位。这类事故史上最著名的案例:盖姆丽游戏《文明》里的甘地(好战值 255 再减,卷回极端好战)、YouTube 点赞数破 2^31(2014 年后必须换 int64)。
两大流派的对策:
| 流派 | 行为 | 代表 |
|---|---|---|
| 不设防 | 静默溢出(卷符号位)——快,但埋雷 | C/C++(有符号溢出是未定义行为)/Java(静默卷回) |
| 防爆仓 | 越界直接抛异常/自动扩容 | Python(int 无限精度)、C# 开启 checked 时 |
四、动手 Demo:三幕剧
print(int(3.9), int(-3.9)) # 3 -3 朝零截断
print(2 ** 31 - 1) # 2147483647 int32 的天花板
print(2147483647 + 1) # 2147483648 Python:不溢出,老实变大
print(1e308 * 10) # inf 浮点上溢:表示不下了,给无穷
看到什么算成功:3 -3、2147483647、2147483648、inf。
读结果:Python 的 int 没有天花板(2147483648 老实写出来),同样的代码在 C/Java 里会卷成 -2147483648——语言不同、后果不同;浮点倒是会溢出,但方式体面(变成 inf 无穷大)。写 Python 时别丢了敬畏:对接的数据库、文件格式、硬件寄存器全是定长整数,Python 帮你算对了,存进去照样可能溢。
五、30 秒实验
跑 int("3.7")——报 ValueError!int() 转字符串时只认纯整数字符串(int("3") 才行),带小数点的要先 float("3.7") 再取整。字符串转数和数转数是两条不同的路,30 秒记牢。
六、常见误区
- 【自动转换很聪明,什么都能自动】——错。它只办【升档】(不丢信息)的;float 转 int、长字符串转数字都必须显式做——这是设计而不是限制。
- 【int(3.7) 是四舍五入】——错。朝零截断得 3;四舍五入是 round(3.7) 得 4。两者差 1,账目对不上多半栽在这。
- 【Python 整数无限精度,所以永远不用管溢出】——错。Python 之外的世界全是定长整数:数据库列类型、C 扩展、网络协议、文件格式——出了 Python 边界,爆仓照旧。
自测三问(点开对答案)
1. **为什么自动转换只【升】不【降】?** 答:升级(小类型转大类型)不丢信息,编译器能替你保证正确;降级会截断丢数据,必须显式声明由程序员负责。 2. **int32 的 2147483647 加 1 在 C/Java 里结果是什么?为什么?** 答:-2147483648——31 位数值位装满,进位卷进符号位(Python 里则是诚实的 2147483648,因为 int 不封顶)。 3. **int("3.7") 为什么报错?** 答:int() 对字符串只接受纯整数写法;带小数点要先 float() 转换再取整——字符串转换与数值转换是两条路。- ☐ 下一节(3-13 关联数组、哈希函数)
相关笔记
- 整数与浮点数 —— 范围封顶与近似的上一课
- C++ 2.4.1 · 数值类型 —— 类型分档的语法面
- 树莓派 6-3 · Python 变量与运算符 —— input() 返回字符串、接数值要转换
- ⌨️ 编程原理 · 索引 —— 全课路线图