当前位置: 首页 > 科技观察

在Java中,整数的绝对值不一定是正数!!!

时间:2023-03-20 15:59:11 科技观察

绝对值是指数轴上某个数对应的点到原点的距离。因此,在数学领域,正数的绝对值就是这个数本身,负数的绝对值应该是它的相反数。这几乎是所有人都知道的事情。在Java中,如果想获取一个数的绝对值,可以使用java.lang.Math中的abs方法。这个类有四个重载的abs方法,分别是:publicstaticintabs(inta){return(a<0)?-a:a;}publicstaticlongabs(longa){return(a<0)?-a:a;}publicstaticfloatabs(floata){return(a<=0.0F)?0.0F-a:a;}publicstaticdoubleabs(doublea){return(a<=0.0D)?0.0D-a:a;}以上四种方法返回int类型的绝对值,分别是long、float和double。方法中的逻辑也很简单,无非就是直接返回一个整数,对返回的负数取反数。所以,基于上面的所有知识,我们经常直接使用Math.abs来取一个数的绝对值。在我们的代码中,有很多这样的例子。比如我们需要用订单号来分库分表,但是订单号是字符串类型的,所以我们需要获取这个字符的hashCode,因为hashCode可能是负数,那么就取绝对值的hashCode,然后用这个值用来对分表号取模:Math.abs(orderId.hashCode())%1024;但是,上面的逻辑是有问题的!!!因为在非常特殊的情况下,上面的代码会得到一个负值。在这种非常特殊的情况下,当hashCode为Integer.MIN_VALUE,即整数所能表达的最小值时,可以验证代码:publicstaticvoidmain(String[]args){System.out.println(Math.abs(Integer.MIN_VALUE));}执行上面的代码,结果是:-2147483648很明显,这是一个负数!!!为什么会这样?这从Integer的取值范围开始。int的取值范围是-2^31——(2^31)-1,即-2147483648到2147483647。那么,我们在用abs取绝对值的时候,就是要得到-2147483648的绝对值,那应该是2147483648,但是2147483648大于2147483647,也就是超出了int的取值范围。这时候就会发生边界越界。2147483647的二进制补码表示为:01111111111111111111111111111111+1得到:10000000000000000000000000000000这个二进制是-2147483648的补码。虽然,这种情况发生的概率很低,只有取绝对值的数为-2147483648时,得到的数仍然是负数。那么,如何解决这个问题呢?既然认为是越界导致最终结果变成负数,那么解决越界问题就可以了,即将int类型转为long类型再取绝对值,这样就不会出界了。例如,如果我们将分表逻辑更改为Math.abs((long)orderId.hashCode())%1024;这将是万无一失的。可以执行如下代码:publicstaticvoidmain(String[]args){System.out.println(Math.abs((long)Integer.MIN_VALUE));}结果为:2147483648以上,这就是要补的知识点今天介绍上来。但是,一定要记住,在取long类型的绝对值时,这种情况可能真的存在!只是发生的概率较低,但只要有他存在,就有可能发生!