long类型对long.min_value取反结果仍为自身,这是由64位二进制补码表示决定的确定性行为;实际应用中需用math.negateexact或显式判断规避该边界情况。

Java中long类型取反(即一元负号-运算)在边界值上不会“溢出”,但对Long.MIN_VALUE取反会得到它自身,这是由二进制补码表示和符号位翻转规则决定的,不是bug,而是符合IEEE/Java规范的确定性行为。
理解long的补码表示与取反逻辑
long是64位有符号整数,采用二进制补码存储。取反操作-x等价于0L - x,底层执行的是补码减法:先对x按位取反再加1(即求补码的相反数)。关键点在于:
-
Long.MAX_VALUE(0x7fffffffffffffffL)取反后为-9223372036854775807L,结果正常,符号位变为1,数值部分正确 -
Long.MIN_VALUE(0x8000000000000000L)是补码中唯一的“无正对应”的负数:它的绝对值超出了正数可表示范围(正数最大只能到2^63-1),因此-Long.MIN_VALUE == Long.MIN_VALUE
实际代码中如何安全处理边界取反
若业务逻辑依赖取反后值严格不等于原值(例如做对称ID映射、哈希扰动等),需显式规避Long.MIN_VALUE:
- 判断是否等于
Long.MIN_VALUE,再分支处理:if (x == Long.MIN_VALUE) { /* 特殊逻辑,如抛异常、返回默认值、或用BigInteger */ } - 使用
Math.negateExact(long)(Java 8+):该方法在遇到Long.MIN_VALUE时抛出ArithmeticException,比静默出错更利于问题暴露 - 若需数学意义上的“绝对值相反数”,且允许大数,可用
BigInteger.valueOf(x).negate().longValueExact()(注意仍会抛异常,但语义更清晰)
验证与调试建议
不要仅凭直觉假设取反总是可逆。调试时建议:
- 用单元测试覆盖
Long.MIN_VALUE、Long.MAX_VALUE、0L、1L、-1L等典型值 - 打印十六进制值辅助理解:
System.out.printf("0x%016x%n", Long.MIN_VALUE); // 输出 0x8000000000000000 - 避免在循环索引、分页偏移量、时间戳差值等场景中隐含依赖“取反必变号”这一假设
延伸:为什么没有类似int的Integer.MIN_VALUE问题?
本质相同——int的Integer.MIN_VALUE(-2147483648)取反也等于自身。这不是long特有,而是所有有符号二进制补码整数类型的共性。区别仅在于long的边界值更大,更容易被忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











