math.abs(integer.min_value) 返回负数;因-2147483648的绝对值2147483648超出int范围(-2147483648~2147483647),补码溢出后仍为-2147483648。

Math.abs(int) 遇到 Integer.MIN_VALUE 会返回负数
这是 Java 中少有「输入合法、调用正确、结果反直觉」的陷阱。当你对 Integer.MIN_VALUE(即 -2147483648)调用 Math.abs(),它不返回正数,而是原封不动返回 -2147483648。
原因很简单:32 位补码中,Integer.MIN_VALUE 的绝对值是 2147483648,但超出了 int 的最大值 Integer.MAX_VALUE(2147483647),溢出后回绕成负数。JDK 文档明确写了这个行为,但很多人没细读。
- 常见错误现象:
Math.abs(x) 竟然为 <code>true,导致后续逻辑(比如判断非负、取模、数组索引)崩掉 - 典型使用场景:做哈希桶索引(
Math.abs(x % capacity))、归一化坐标、校验输入范围 - 别指望加个
(long)强转就能躲开——Math.abs((long) Integer.MIN_VALUE)是安全的,但如果你忘了转,还是踩坑
Math.abs(long) 同样不免疫 Long.MIN_VALUE
同理,Math.abs(long) 对 Long.MIN_VALUE(-9223372036854775808L)也返回负值。64 位补码下,它的绝对值无法用 long 表示。
这个问题比 int 版更隐蔽:因为 long 值通常来自计算或外部输入,开发者很少手动写 Long.MIN_VALUE,但一旦上游传入(比如解析 JSON 或数据库字段为 NULL 时默认填最小值),就可能触发。
立即学习“Java免费学习笔记(深入)”;
- 参数差异:
Math.abs(int)和Math.abs(long)都未做溢出检查,语义上就是“按位取反加一”,不是“数学意义的绝对值” - 性能影响:无额外开销,但错误结果带来的修复成本远高于预防成本
- 兼容性没问题——这行为从 JDK 1.0 就存在,改不了,也不该改
安全替代方案:用条件判断代替无脑调用
最直接的办法,就是别依赖 Math.abs() 的“绝对安全”假象。尤其在涉及索引、循环边界、比较逻辑的地方,显式处理极值。
- 对
int:用x == Integer.MIN_VALUE ? 0 : Math.abs(x)(若业务允许映射为 0);或升级为Math.abs((long) x)再转回int(注意二次溢出风险) - 对
long:必须用x == Long.MIN_VALUE ? 0L : Math.abs(x),或者直接用BigInteger.valueOf(x).abs().intValue()(仅限低频路径) - 别用
x ——对 <code>Integer.MIN_VALUE,-x同样溢出,结果还是负数
容易被忽略的间接调用点
你以为只在自己写的 Math.abs() 里踩坑?错。很多工具方法底层偷偷用了它。
例如 Guava 的 Ints.constrainToRange(x, 0, 100)、Apache Commons Lang 的 NumberUtils.max(a, b)(当含负数时可能触发 abs)、甚至某些 JSON 库反序列化数字时做的规范化处理——都可能暗藏这个逻辑分支。
- 排查方式:全局搜
Math\.abs,再顺藤摸瓜看哪些地方接收了用户可控输入 - 测试要点:单元测试必须覆盖
Integer.MIN_VALUE和Long.MIN_VALUE,不能只测-1、0、1 - 最麻烦的是:这种 Bug 往往在线上低概率出现,日志里只显示“索引越界”或“非法参数”,根本看不出源头是
abs










