
本文通过分析int类型乘法溢出的具体案例,揭示其底层二进制位移与截断机制,帮助开发者理解为何16+256连乘6次16后得到268435456(即2²⁸),而非数学预期值。
在Java中,int是有符号32位整数,取值范围为 −2,147,483,648 到 2,147,483,647(即 −2³¹ 至 2³¹−1)。当运算结果超出该范围时,不会抛出异常,而是发生静默溢出(silent overflow)——系统仅保留低32位二进制结果,高位被直接截断。
以你的代码为例:
public static void main(String[] args) {
int x = 16 + 256; // = 272
for (int i = 0; i < 6; i++) {
x *= 16; // 等价于左移4位(x <<= 4)
}
System.out.println(x); // 输出:268435456
}我们追踪其二进制演化过程(使用32位补码表示,高位补0):
- 初始值:272 → 00000000 00000000 00000001 00010000(共32位)
- 每次 x *= 16 等价于 逻辑左移4位(因 16 = 2⁴),即末尾补4个0,高位溢出部分被丢弃。
| 步骤 | 十进制值 | 二进制(高→低,32位) | 关键变化 |
|---|---|---|---|
| 初始 | 272 | 00000000 00000000 00000001 00010000 | — |
| ×16¹ | 4352 | 00000000 00000000 00010001 00000000 | 左移4位 |
| ×16² | 69632 | 00000000 00000001 00010000 00000000 | — |
| ×16³ | 1,114,112 | 00000000 00010001 00000000 00000000 | — |
| ×16⁴ | 17,825,792 | 00000001 00010000 00000000 00000000 | — |
| ×16⁵ | 285,212,672 | 00010001 00000000 00000000 00000000 | 首位1即将溢出 |
| ×16⁶ | 268,435,456 | 00010000 00000000 00000000 00000000 | 最高位1被截断! |
注意第6次左移后:原始高位00010001...中的第一个1已移出32位边界,被彻底丢弃,最终只剩 00010000 后接28个0 → 即 1 × 2²⁸ = 268,435,456。
立即学习“Java免费学习笔记(深入)”;
✅ 关键结论:
- int乘法溢出本质是无符号位移+32位截断,与符号无关(即使结果为正,溢出仍按位处理);
- 16 是 2⁴,因此 x *= 16 等价于 x
- 若需安全计算,应改用 long(64位)或 BigInteger:
long x = 16L + 256L; for (int i = 0; i < 6; i++) x *= 16; System.out.println(x); // 正确结果:272 × 16⁶ = 2,852,126,720
⚠️ 注意:Java 8+ 提供了带溢出检查的数学方法(如 Math.multiplyExact),可主动捕获异常:
try {
int x = 272;
for (int i = 0; i < 6; i++) x = Math.multiplyExact(x, 16);
} catch (ArithmeticException e) {
System.err.println("整数溢出:" + e.getMessage());
}理解二进制位移与截断机制,是避免隐式溢出错误、编写健壮数值计算代码的基础。









