您的位置 首页 java

Math.abs 竟然返回了负数?

事情是这样的。

某一天扯扯群里发来一段代码:

读者提问道,为啥这个 pos 还要判断一下?

这代码一看我就熟悉,RocketMQ 的源码,如果你看过 源码 你会发现到处都有这样的判断。

想着已经取绝对值了,然后再取余,肯定是正数啊,这 if(pos<0) 不就是多余的判断吗?

那我们先看一下 Math.abs 的源码:

可以看到,十分简单粗暴,再结合一下 int ,不知道大家是否已经发现了问题?

不卖关子了。

int 的最大值是(2^31) -1,而最小值是-2^31,所以按照 abs 的逻辑,如果a是最小值,则最小值前面加个负数就变成了 2^31,而 int 所能表示的最大值是 (2^31) -1,这比最大值还大了个 1,导致向上溢出,所以此时得到的结果还是最小值。

你可以试着运行一下上面的结果,所以说 Math.abs 得到的不一定是正数!所以 RocketMQ 的这个判断是有必要滴。

int 是 32 位,为了便于演示,我就拿 8 位来举例子,反正道理是一样的。

在 Java 中的数字的实现都是有符号位的,不像 C 有个 unsigned 来表示无符号,有符号的数字实现是用最高位来表示符号位,1表示负数,0表示正数。

回到上面的例子,要表示的正数比最大值还大1,那就把最大值加个1呗。

图中就是向上溢出得到了最小值,所以最小值取绝对值得到的值比最大值大一,导致向上溢出,又变成了最小值。

因此 Math.abs(Integer.MIN_VALUE) = Integer.MIN_VALUE

那为什么 8 位表示的是-128到127而不是-127到128?

8位 二进制 ,一共有 2^8=256 个坑位,所以能表示 256 个数字,理论上随便怎么表示都ok,你表示-254~1都行。

但是我们规定无符号数是 0~255,有符号数规定是-128 ~ 127。

如果有符号数要规定成-127到128并不是不行,但是这样就比较麻烦。

按照-128 ~ 127这样的实现, 我们只要通过最高位就可以判断一个数的正负 ,而-127~128就需要排除128这个特例,也就是之前只需要判断最高位,现在变成需要判断最高位为 1 且其它位不全是 0 才是负数。

这样电路设计也要变复杂了,所以我们规定是 -128到127,32位也同理。

Math.absExact

这种出错了但是没有提示的 Math.abs 肯定是不好的,所以在 JDK 15 出了个 Math.absExact

也就是会抛错,而不是返回个错误的结果。

最后

有位群友还说面百度的时候被问过。

面试题是真的多,防不胜防,所以要抓清源头和本质,以不变应万变。

转载自:

原创:yes的练级攻略

文章来源:智云一二三科技

文章标题:Math.abs 竟然返回了负数?

文章地址:https://www.zhihuclub.com/177248.shtml

关于作者: 智云科技

热门文章

网站地图