在Java里,int类型使用32位补码存储,最大值为2147483647,最小值为-2147483648。当两个整数相乘的结果超过这一区间,就会发生溢出,得到看似错误的数值。从二进制层面看,乘法可以拆解为移位与加法,而位移越界会直接改变符号位。

一、Java整数乘法的溢出表现
先看一段简单代码,两个正数相乘却得到了负数:
public class MulOverflow {
public static void main(String[] args) {
int a = 100000;
int b = 100000;
int c = a * b; // 预期100亿,实际溢出
System.out.println(c); // 输出 -727379968
}
}
这是因为100000乘以100000等于10000000000,远超int上限,二进制高位被截断,剩下的位恰好被解读成了负数。
二、补码与溢出的二进制本质
Java的int采用补码:最高位为符号位,0表示正,1表示负。乘法在CPU中近似于将乘数按被乘数的每一位做左移后累加。
1. 左移相当于乘2
例如整数5的二进制为000...0101,左移1位变成10,即十进制10。若一直左移超过31位,符号位会被新数据覆盖。
int x = 1; int y = x << 31; // 左移31位,符号位变1,结果为-2147483648 System.out.println(y);
2. 乘法中的隐式位移
当执行a * b时,若b的第k位为1,就相当于a左移k位再相加。若某次左移使结果位数超过32,超出部分直接丢弃。
| 十进制 | 二进制(简写) | 说明 |
|---|---|---|
| 3 | 0011 | 原数 |
| 3<<2 | 1100 | 左移2位得12 |
| 溢出例 | 高位截断 | 超32位部分消失 |
三、如何用位移理解并避免溢出
若确定数值范围较小,可用long接收乘法结果:
int a = 100000; int b = 100000; long c = (long) a * b; // 正确得到10000000000 System.out.println(c);
另外,使用Math.multiplyExact可在溢出时抛异常,便于排查:
import java.lang.Math; int r = Math.multiplyExact(100000, 100000); // 抛出ArithmeticException
四、小结
整数乘法溢出并非随机错误,而是32位补码空间有限、二进制左移相加后高位被丢弃的必然结果。掌握位移与补码规则,就能在编码时预判风险,选择合适的类型或工具方法规避问题。