zoukankan      html  css  js  c++  java
  • Java中在时间戳计算的过程中遇到的数据溢出问题

    背景

    今天在跑定时任务的过程中,发现有一个任务在设置数据的查询时间范围异常,出现了开始时间戳比结束时间戳大的奇怪现象,计算时间戳的代码大致如下。

    package com.lingyejun.authenticator;
    
    public class IntegerTest {
    
        public static void main(String[] args) {
            long endTime = System.currentTimeMillis();
            long startTime = endTime - 30 * 24 * 60 * 60 * 1000;
    
            System.out.println("end   : " + endTime);
            System.out.println("start : " + startTime);
        }
    }
    

     先放出结论:因为java中整数默认是int类型,在计算的过程中30 * 24 * 60 * 60 * 1000计算结果大于Integer.MAX_VALUE,所以出现了数据溢出,从而导致了计算结果不准确的问题。

    验证

    我们将上面的代码稍稍改造一下,方便我们确认定位问题,调整后的代码如下:

    package com.lingyejun.authenticator;
    
    public class IntegerTest {
    
        public static long calcStartTime(long endTime, long minusMills) {
            System.out.println("end  : " + endTime + " minus mills : " + minusMills);
            long startTime = endTime - minusMills;
            System.out.println("start: " + startTime);
            return startTime;
        }
    
        public static void main(String[] args) {
            long nowTime = System.currentTimeMillis();
            long a = 30 * 24 * 60 * 60 * 1000;
            calcStartTime(nowTime, a);
        }
    }  

    结果如下:

    end  : 1560869539864 minus mills : -1702967296
    start: 1562572507160

    这和我们的预期不一样,因为30 * 86400000 = 2592000000,但是计算出来却是:-1702967296。

    到这里想必大家都知道原因了,这是因为java中整数的默认类型是整型int,而int的最大值是2147483647,

    在代码中java是先计算右值,再赋值给long变量的。在计算右值的过程中(int型相乘)发生溢出,然后将溢出后截断的值赋给变量,导致了结果不准确。

    将代码做一下小小的改动,再看一下。

    package com.lingyejun.authenticator;
    
    public class IntegerTest {
    
        public static long calcStartTime(long endTime, long minusMills) {
            System.out.println("end  : " + endTime + " minus mills : " + minusMills);
            long startTime = endTime - minusMills;
            System.out.println("start: " + startTime);
            return startTime;
        }
    
        public static void main(String[] args) {
            long nowTime = System.currentTimeMillis();
            long a = 30 * 24 * 60 * 60 * 1000L;
            calcStartTime(nowTime, a);
        }
    }
    

    结果为

    end  : 1560869539864 minus mills : 2592000000
    start: 1558277539864
    

    似乎这样应该就没有什么问题了,但是这样就真的保险了吗,如果我要把30调整为24856(Integer.MAX_VALUE / 86400 = 24855),即改为:long a = 24856 * 24 * 60 * 60 * 1000L 那么同样会出现溢出。

    因为java的运算规则从左到右,再与最后一个long型的1000相乘之前就已经溢出,所以结果也不对,正确的方式应该如下:long a = 24856L * 24 * 60 * 60 * 1000。

    package com.lingyejun.authenticator;
    
    public class IntegerTest {
    
        public static long calcStartTime(long endTime, long minusMills) {
            System.out.println("end  : " + endTime + " minus mills : " + minusMills);
            long startTime = endTime - minusMills;
            System.out.println("start: " + startTime);
            return startTime;
        }
    
        public static void main(String[] args) {
            long a = 30L * 24 * 60 * 60 * 1000;
            calcStartTime(nowTime, a);
        }
    }
    

    参考文章:

    https://njucz.github.io/2017/08/16/java-int%E6%BA%A2%E5%87%BA%E6%80%BB%E7%BB%93/ 

  • 相关阅读:
    LeetCode 295. Find Median from Data Stream (堆)
    LeetCode 292. Nim Game(博弈论)
    《JavaScript 模式》读书笔记(4)— 函数2
    《JavaScript 模式》读书笔记(4)— 函数1
    《JavaScript 模式》读书笔记(3)— 字面量和构造函数3
    《JavaScript 模式》读书笔记(3)— 字面量和构造函数2
    《JavaScript 模式》读书笔记(3)— 字面量和构造函数1
    《JavaScript 模式》读书笔记(2)— 基本技巧3
    《JavaScript 模式》读书笔记(2)— 基本技巧2
    《JavaScript 模式》读书笔记(2)— 基本技巧1
  • 原文地址:https://www.cnblogs.com/lingyejun/p/11048501.html
Copyright © 2011-2022 走看看