混合浮点数和长计算产生错误答案,没有编译器警告

时间:2012-11-28 19:19:22

标签: java

令人惊讶的是,以下代码为startTime输出了错误的值?

public class Temp {
    public static void main(String args[]){
       float duration = (float) 2.0;
       long endTime = 1353728995;
       long startTime = 0;
       startTime = (long) (endTime - duration);
       System.out.println(startTime);
}
}

4 个答案:

答案 0 :(得分:4)

它不输出错误的值。它只输出一个你不期望的值。

重要的是这里发生的事情:

endTime - duration

实际评估为:

(float) endTime - duration;

这就是数据丢失的地方。与1353728995最接近的float值为1353729024,与减去2的结果最接近的float仍为 1353729024。

这一切都遵循语言规范。 JLS section 15.8.2(加法运算符)声明二进制数字促销应用于操作数。

Section 5.6.2 (binary numeric promotion)就像这样开始:

  

当运算符将二进制数字提升应用于一对操作数时,每个操作数必须表示可转换为数字类型的值,以下规则适用:

     
      
  • 如果任何操作数属于引用类型,则进行拆箱转换(第5.1.8节)。

  •   
  • 应用扩展基元转换(第5.1.2节)来转换由以下规则指定的一个或两个操作数:

         
        
    • 如果任一操作数的类型为double,则另一个操作数转换为double。

    •   
    • 否则,如果任一操作数的类型为float,则另一个操作数将转换为float

    •   
    • ...

    •   
  •   

遵循这些规则,long的{​​{1}}值会转换为endTime,然后在float算术中执行减法。

请记住,float仅提供7个有效数字,其余部分相当明显,我希望。

请注意,如果没有将结果转换为float,编译器会更清楚地发生了什么:

long

这清楚地表明结果将是Test.java:8: error: possible loss of precision long startTime = endTime - duration; ^ required: long found: float 1 error ,这应该引发关于如何执行操作的警告,以及预期的准确性。

答案 1 :(得分:0)

如果你想要长的结果,可以将浮动点转换为long。

 float duration = (float) 2.0;
    long endTime = 1353728995;
    long startTime = 0;
    startTime = endTime - (long)duration;
    System.out.println(startTime);

输出:

1353728993

如果你没有强制转换,它将被视为浮点值,结果将是意外的。

答案 2 :(得分:0)

这都是因为浮点表示法。

当您使用float作为至少一个参数执行任何操作时,所有参与号码将被提升为float。在此处,endTime在减法之前被转换为1.35372902E9,因为它使用指数表示的IEEE 754 notation此表示形式不是很精确,因此您会看到不同的结果。

如果你做的话

    startTime =  (endTime - (long)duration);

然后使用long进行操作,你应该得到预期的结果。

答案 3 :(得分:0)

doublefloat经常会导致精确度下降。这可能是你在这里看到的。

请注意,您实际上在计算:

startTime = (long) ((float)endTime - duration);

因为编译器会自动将long转换为float用于减法。这是有据可查的,一些好的代码检查器实际上会警告你这一点。

你可能想要的是:

startTime = endTime - (long)duration;

但是你应该这么说。

您也可以使用

获得正确的答案
startTime = (long) (endTime - (double)duration);

但这也不是安全