严防Math.abs()返回负数
2017-05-12 13:32
148 查看
最近有个线上spark streaming程序跑着跑着就挂了,调查了一番,发现了一个平时大家都不太注意的问题。
看了日志,抛出的异常如下:
找到出错代码如下:
具体来说就是
好奇怪啊,这里的
先别下结论,我们首先知道的是:模运算的值如果为负数,则两个运算数必须要有一个为负数,并且我们可以百分之百肯定radix不可能为负数,所以按照神探福尔摩斯所说的:排除一切不可能的,剩下的即使再不可能,那也是真相,那我们就先把『真相』的帽子戴在这个绝对值的头上。
去看了一下JDK中关于Math.abs的文档:
果然找到了真相:如果是对Integer(Long也一样)取绝对值,如果原值是Integer.MIN_VALUE,则得到的绝对值和原值相等,是一个负数。为什么呢?因为你看看abs的实现,它很简单,如果原值是正数,直接返回,如果是负数,就简单地在前面加个负号。然而Integer的范围是[-2147483648,2147483647],如果原值是最小的值取反之后不就越界了嘛。
空口无凭,我们做个试验:
可以看到,试验结果与现场完全吻合。
其实这个坑我很早以前就知道了,并不是什么稀奇的问题,但是这次还是犯错了,所以在此记录一下,严防以后再犯,也和大家分享一下,希望大家以后注意。
看了日志,抛出的异常如下:
java.lang.ArrayIndexOutOfBoundsException: -2 at com.xiaomi.poppy.hbase.HBaseUtil.getHashPrefix(HBaseUtil.java:58) at com.xiaomi.xmpush.spark.hbase.HBaseProcessor.buildRowKey(HBaseProcessor.java:220) at com.xiaomi.xmpush.spark.hbase.HBaseProcessor.buildRequest(HBaseProcessor.java:203) at com.xiaomi.xmpush.spark.hbase.HBaseProcessor.writeCounter(HBaseProcessor.java:126) at com.xiaomi.xmpush.main.PushSparkStatMain$4$1.call(PushSparkStatMain.java:102) at com.xiaomi.xmpush.main.PushSparkStatMain$4$1.call(PushSparkStatMain.java:97) at org.apache.spark.api.java.JavaRDDLike$$anonfun$foreach$1.apply(JavaRDDLike.scala:330) at org.apache.spark.api.java.JavaRDDLike$$anonfun$foreach$1.apply(JavaRDDLike.scala:330) at scala.collection.Iterator$class.foreach(Iterator.scala:727) at scala.collection.AbstractIterator.foreach(Iterator.scala:1157) at org.apache.spark.rdd.RDD$$anonfun$foreach$1$$anonfun$apply$28.apply(RDD.scala:894) at org.apache.spark.rdd.RDD$$anonfun$foreach$1$$anonfun$apply$28.apply(RDD.scala:894) at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1854) at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1854) at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:66)
找到出错代码如下:
public static String getHashPrefix(String input, int length) { int hash = Math.abs(getHashCodeForString(input)); final int radix = CHAR_MAP.length; StringBuffer sb = new StringBuffer(); for (int index = 0; index < length; ++index) { sb.append(CHAR_MAP[hash % radix]); hash /= radix; } return sb.toString(); }
具体来说就是
sb.append(CHAR_MAP[hash % radix]);这句中的
hash % radix得到了一个负数(-2),导致数组越界。
好奇怪啊,这里的
hash是一个绝对值,按理来说是非负数才对,而
radix是一个数组的长度,已经固定为62了,
hash % radix怎么可能为负数。
先别下结论,我们首先知道的是:模运算的值如果为负数,则两个运算数必须要有一个为负数,并且我们可以百分之百肯定radix不可能为负数,所以按照神探福尔摩斯所说的:排除一切不可能的,剩下的即使再不可能,那也是真相,那我们就先把『真相』的帽子戴在这个绝对值的头上。
去看了一下JDK中关于Math.abs的文档:
/** * Returns the absolute value of an {@code int} value. * If the argument is not negative, the argument is returned. * If the argument is negative, the negation of the argument is returned. * * <p>Note that if the argument is equal to the value of * {@link Integer#MIN_VALUE}, the most negative representable * {@code int} value, the result is that same value, which is * negative. * * @param a the argument whose absolute value is to be determined * @return the absolute value of the argument. */ public static int abs(int a) { return (a < 0) ? -a : a; }
果然找到了真相:如果是对Integer(Long也一样)取绝对值,如果原值是Integer.MIN_VALUE,则得到的绝对值和原值相等,是一个负数。为什么呢?因为你看看abs的实现,它很简单,如果原值是正数,直接返回,如果是负数,就简单地在前面加个负号。然而Integer的范围是[-2147483648,2147483647],如果原值是最小的值取反之后不就越界了嘛。
空口无凭,我们做个试验:
可以看到,试验结果与现场完全吻合。
其实这个坑我很早以前就知道了,并不是什么稀奇的问题,但是这次还是犯错了,所以在此记录一下,严防以后再犯,也和大家分享一下,希望大家以后注意。
相关文章推荐
- Math.abs()方法不一定总是返回整数
- Math.abs为Integer.Min_VALUE返回错误的值
- 返回绝对值--Math.Abs 方法
- 由计算机产生0-9之间的随机整数,用户输入猜测的数据,统计用户猜测的次数。产生随机数可以使用Math.rangom()静态方法,该方法返回正的double类型浮点数位于(0.0 1.0】间。
- Math.abs()不能办到的事
- PHP使用ip2long返回负数
- JavaScript Math.abs() 函数
- Mybatis执行Update返回行数为负数
- java函数中math.abs(x)是什么意思
- java中Math.abs(-2147483648)的返回值应该是什么?
- java中常用到的math方法(Math.PI、Math.random()、Math.abs(double)、Math.floor(double)、Math.ceil(double)、Math.round(double))
- Math,random()返回区间内的随机数
- Mybatis执行Update返回行数为负数
- /src/pkg/math/abs.go源码阅读兼谈golang与汇编
- 连续子数组最大和问题(能够处理全是负数,返回子数组的起止索引的O(N)算法)
- 8皇后代码,abs在stdlib中,不在math中
- PHP中大整数取余返回负数的问题
- Math.abs