[英]Mixed float and long calculation yields wrong answer with no compiler warning
以下代碼為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);
}
}
它不會輸出錯誤的值。 它只會輸出您不期望的值。
重要的一點是這里發生了什么:
endTime - duration
實際評估為:
(float) endTime - duration;
那就是數據丟失的地方。 最接近1353728995的float
值是1353729024,最接近2的結果的最接近float
仍然是 1353729024。
這些都遵循語言規范。 JLS第15.8.2節 (加法運算符)指出,二進制數值提升適用於操作數。
第5.6.2節(二進制數字提升)像這樣開始:
當運算符將二進制數字提升應用於一對操作數時,每個操作數必須表示一個可轉換為數字類型的值,以下規則適用:
如果任何操作數是引用類型,則將其進行拆箱轉換(第5.1.8節)。
擴展原語轉換(第5.1.2節)適用於轉換以下規則指定的一個或兩個操作數:
如果一個操作數的類型為double,則另一個將轉換為double。
否則,如果其中一個操作數的類型為
float
,則另一個將轉換為float
。...
因此,遵循這些規則,將endTime
的long
值轉換為float
,然后在float
算法中執行減法。
請記住, float
只能提供7個有效數字,我希望其余的數字非常明顯。
請注意,如果不將結果強制轉換為long
,則編譯器將更清楚地說明發生了什么:
Test.java:8: error: possible loss of precision
long startTime = endTime - duration;
^
required: long
found: float
1 error
這非常清楚地表明結果將是float
,這應該引起警告,說明將如何執行操作以及可以預期的精度。
如果需要,可以將浮點數轉換為long。
float duration = (float) 2.0;
long endTime = 1353728995;
long startTime = 0;
startTime = endTime - (long)duration;
System.out.println(startTime);
輸出:
1353728993
如果不進行強制轉換,則將其視為浮點值,並且結果將是意外的。
這都是因為浮點符號。
當使用float
作為至少一個參數執行任何操作時,所有參與的數字都會提升為float
。 在這里,您的endTime
將在減法之前轉換為浮點數1.35372902E9
,因為它使用了指數表示IEEE 754 notation
的IEEE 754 notation
。 這種表示方式不是很精確,因此您會看到不同的結果。
好像在做
startTime = (endTime - (long)duration);
然后使用它的時間很長,您應該會得到預期的結果。
double
和float
經常會降低精度。 您在這里看到的可能是哪種。
請注意,您實際上正在計算:
startTime = (long) ((float)endTime - duration);
因為編譯器會自動將long轉換為float以進行減法。 這是有據可查的,並且一些出色的代碼檢查器實際上會警告您。
您可能想要的是:
startTime = endTime - (long)duration;
但是那你應該這樣說。
您可能還可以通過使用獲得正確的
startTime = (long) (endTime - (double)duration);
但這也不安全 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.