基于Java Long数据类型时间戳的表达式,提供意外输出

时间:2016-02-23 18:42:32

标签: java

我有一个简单的基于时间戳的表达式,它从JSONObject中提取值。以下是代码的摘录

Long cur_trig = Long.parseLong(api_content.get("timestamp").toString());
Long last_trig  = Long.parseLong(trigger_info.get(key)); 
frequency = 60.0
if ( last_trig + frequency*1000 > cur_trig) {  
  System.out.println("Too early for an alert");
}

上述代码不符合if条件,即使理论上是正确的。

Long curr_trig = 1455213601000L;
Long last_trig = 1455213600000L;
double freq = 60.0;
if (last_trig + freq * 1000 > curr_trig) {
    System.out.println("Yup its correct");
}

在这种情况下,它完全正常。我已经手动打印了第一种情况下的值,数字确实加起来了。奇怪的是,我注意到,如果我手动对类值进行类型转换:

Long next_trig_time  = (long) (last_trig + alert.frequency*1000); 
if ( next_trig_time > cur_trig) { 
    System.out.println( "Its fine!");
}

这似乎工作正常。我无法理解的是,在评估表达式时,Java是否应自动将值转换为更高数据类型?或至少抛出警告/错误?

编辑1: 根据建议,我添加了以下内容

boolean notified = last_trig + alert.frequency*1000 > cur_trig ;
if (notified) { 
    System.out.println( "Its fine!");
}

与第一种情况相比,这没有任何区别。

编辑2: 使用long代替Long

long last_trig  = Long.parseLong(trigger_info.get(key));
long cur_trig = Long.parseLong(api_content.get("timestamp").toString());
frequency = 60.0
boolean notified = last_trig + frequency*1000 > cur_trig ;
if (notified) { 
   System.out.println( "Its fine!");
}

令人惊讶的是,即使这样也行不通。

编辑3: 这是一个可重现的问题

JSONObject js = new JSONObject();
js.put("last_trig","1455213601000");
js.put("cur_trig", "1455213600000");

Long last_trig = Long.parseLong(js.get("last_trig").toString());
Long cur_trig = Long.parseLong(js.get("cur_trig").toString());
float frequency = (float) 60.0;
Long next_trig_time = (long) (last_trig + frequency*1000);

if (last_trig + frequency*1000 > cur_trig) {  
    System.out.println("[Case 1] Too early for an alert");
}
else {
    System.out.println("[Case 1] Too late for an alert");
}

if ( next_trig_time > cur_trig) {  
    System.out.println("[Case 2] Too early for an alert");
}
else {
    System.out.println("[Case 2] Too late for an alert");
}

当前输出:

  

[案例1]警报太迟了   [案例2]警报太早

预期产出:

  

[案例1]警报太早了   [案例2]警报太早

2 个答案:

答案 0 :(得分:1)

简短回答:java中的float没有足够的精度来做你想做的事。基本上,6-7有效数字是最大值。

解决方法,使用双打。

    Long last_trig = Long.parseLong(js.get("last_trig").toString());
    Long cur_trig = Long.parseLong(js.get("cur_trig").toString());
    double frequency = 60.0;
    double v = last_trig + frequency * 1000;

    if (v > cur_trig) {
        System.out.println("[Case 1] Too early for an alert");
    }
    else {
        System.out.println("[Case 1] Too late for an alert");
    }

    Long next_trig_time = (long) v;
    if ( next_trig_time > cur_trig) {
        System.out.println("[Case 2] Too early for an alert");
    }
    else {
        System.out.println("[Case 2] Too late for an alert");
    }

[案例1]警报太早了 [案例2]警报太早

有趣的东西:

    long l = 0x123456712345678L;
    float d = l;
    long lAgain = (long) d;
    System.out.println(lAgain == l);
猜猜它打印的是什么?

答案 1 :(得分:0)

让我们比较两个代码片段:

你说这有用......:

Long curr_trig = 1455213601000L;
Long last_trig = 1455213600000L;
double freq = 60.0;
if (last_trig + freq * 1000 > curr_trig) {
    System.out.println("Yup its correct");
}

......而且这失败了:

Long last_trig = Long.parseLong(js.get("last_trig").toString());
Long cur_trig = Long.parseLong(js.get("cur_trig").toString());
float frequency = (float) 60.0;
if (last_trig + frequency*1000 > cur_trig) {  
    System.out.println("[Case 1] Too early for an alert");
}

这里最重要的区别是frequency的类型。它会影响last_trig + frequency*1000的类型,而float似乎无法正确处理您的号码。它会将结果视为1.45521364E12(由于flating point inaccuracies,我认为它不是1.45521366E12)。右侧也可能会“升级”为float,以便对其进行比较(reference)和(float)cur_trig也是1.45521366E12

你可以通过以下方式解决这个问题:

  • frequency更改为double

    double frequency = 60.0;
    
  • 投射到double

    if (last_trig + (double)(frequency * 1000) > cur_trig) {
    
  • 允许相等的值(因此将支持不准确的float):

    if (last_trig + frequency * 1000 >= cur_trig) {