神奇在 Double 转换异常
异常
线上收到告警, 有以下异常
java.lang.ArithmeticException: input is infinite or NaN
at com.google.common.math.DoubleMath.roundIntermediate(DoubleMath.java:59)
at com.google.common.math.DoubleMath.roundToLong(DoubleMath.java:156)
.......................
代码
对应的业务代码如下:
public static Long formatLongDefaultNegativeOne(Object obj) {
String objStr = String.valueOf(obj);
return !StringUtils.isBlank(objStr) &&
!objStr.equals("NaN") &&
!objStr.equals("Infinity") &&
!objStr.equals("null") ?
DoubleMath.roundToLong(Double.valueOf(String.valueOf(obj)), RoundingMode.HALF_EVEN) :
-1L;
}
抛出异常的地方做了如下判断:
......
if (!isFinite(x)) {
throw new ArithmeticException("input is infinite or NaN");
}
......
......
//判断是否在有限范围内
static boolean isFinite(double d) {
return getExponent(d) <= MAX_EXPONENT;
}
getExponent(d)
是获取当前 double
值的指数 . MAX_EXPONENT
是Double
最大值的指数 .
getExponent(d)
的方法注释中有以下说明:
If the argument is NaN or infinite, then the result is Double.MAX_EXPONENT + 1.
If the argument is zero or subnormal, then the result is Double.MIN_EXPONENT -1.
判断不在有效范围内的, 只有两种情况 , double
值是 NaN
或Infinity
.
分析
主要逻辑都在这行代码中:
DoubleMath.roundToLong(Double.valueOf(String.valueOf(obj)))
从上面的异常中可以得知 , DoubleMath.roundToLong
方法得到是 NaN
或 Infinity
. 即 Double.valueOf() == NaN || Double.valueOf() == Infinity
.
业务方法的这个代码 formatLongDefaultNegativeOne(Object obj)
是从上游获取的数值 , 业务上已经限定了只能是数值类型. 对于从上游得到的数值 , 理论上是只有这几种情况了.
- null
- NaN
- Infinity
- 正常数值
对于前三种 , 逻辑上已经做了判断 , 但结果还是抛出了异常 . 说明数值不在上面四种情况里 .
当前也没有想法具体是什么样的数值才能导致这个异常 . 由于缺少线上数据的支撑 , 无法继续下去 . 只能后面再解决定位了.
未完待续.......
后记
线上加了日志, 才终于发现了问题。
这个数据是 -Infinity
。 是负无限 !
本应是正数计算的逻辑里, 竟然出现了负数。 所以一直也没往负数的方向上去想。
找到问题就好解决了 。
- 解决业务里出现负数的业务逻辑
- 不兼容负数场景 , 但要有个 try - catch 。
这个神奇的异常算是结束了。 同时也做了个警醒 , 对待无限 , 有正无限, 还有负无限!
如果文章有帮助到您,请点个赞,您的反馈会让我感到文章是有价值的