令人惊讶的是,以下代码为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);
}
}
答案 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)
double
和float
经常会导致精确度下降。这可能是你在这里看到的。
请注意,您实际上在计算:
startTime = (long) ((float)endTime - duration);
因为编译器会自动将long转换为float用于减法。这是有据可查的,一些好的代码检查器实际上会警告你这一点。
你可能想要的是:
startTime = endTime - (long)duration;
但是你应该这么说。
您也可以使用
获得正确的答案startTime = (long) (endTime - (double)duration);
但这也不是安全。