2015-09-08 49 views
7

我認爲這個問題很直接。但這裏是一個例子。如何測試雙值存儲的值是否適合long? (舍入是,但截斷否)

以下示例正常。我可以採取四捨五入在這裏沒有截斷。

public static void main(String[] args) { 
    double d = 9.9; 
    long l = (long)d; 
    System.out.println(l); 
} 

輸出:

9 

現在數出的遠射程:

public static void main(String[] args) { 
    double d = 99999999999999999999999999999999.9; 
    long l = (long)d; 
    System.out.println(l); 
} 

輸出:

9223372036854775807 

這一個困擾我。我無法繼續使用完全不同的數字。我寧願得到一個錯誤或異常。

有什麼辦法可以在Java中檢測到這個嗎?

回答

12

您可以Long.MIN_VALUELong.MAX_VALUE比較吧:

public static boolean fitsLong(double d) { 
    return d >= Long.MIN_VALUE && d < Long.MAX_VALUE; 
} 

某種程度上更sofisticated的方法是使用BigDecimal

double value = 1234567.9; 
long l = BigDecimal.valueOf(value) 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // 1234568 

double value = 99999999999999999999999999999999.9; 
long l = BigDecimal.valueOf(value) 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // ArithmeticException 

這樣,您就可以控制如何進行舍入。

您可能會問,爲什麼fitsLong中存在嚴格的不等式:d < Long.MAX_VALUE。其實這是因爲Long.MAX_VALUE本身不能表示爲雙數。當您投下(double)Long.MAX_VALUE時,double類型中沒有足夠的精度來表示它,因此選擇的最接近的可表示值爲9223372036854775808.0Long_MAX_VALUE+1.0)。所以它是d <= Long.MAX_VALUE它會返回true爲實際上有點大,因爲在這個比較Long.MAX_VALUE常數提升爲雙重類型的數字。另一方面Long.MIN_VALUE可以完全代表double類型,因此我們在這裏有>=

而且它們也同樣吸引爲什麼以下工作:

double value = -9223372036854775809.9; // Long.MIN_VALUE-1.9 
System.out.println(fitsLong(value)); // returns true 

那是因爲你實際上並沒有減去從Long.MIN_VALUE什麼。請參閱:

double d1 = Long.MIN_VALUE; 
double d2 = -9223372036854775809.9; 
System.out.println(d1 == d2); // true 

雙精度不夠-9223372036854775808-9223372036854775809.9區分,所以它實際上是相同的雙號。在編譯期間它被轉換成二進制形式,而這兩個數字的二進制形式是相同的。因此在編譯程序時,您無法區分-9223372036854775808-9223372036854775809.9是否在源代碼中。

如果你覺得它仍然是問題,從String構建BigDecimal

long l = new BigDecimal("-9223372036854775808.2") 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // ok, -9223372036854775808 

long l = new BigDecimal("-9223372036854775808.9") 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // ArithmeticException 
+0

對於第一種解決方案)其實你不能。我試過了,它不會工作。例如嘗試一個值9223372036854775809.9,將Long.MAX_VALUE增加2.9。會過去的。 – arenaq

+0

你是對的,但當我使用9223372036854775807.0這兩種解決方案。 FitsLong讓我真實,BigDecimal拋出一個異常。這個雙重價值可能會保持不變,但至少有一個解決方案必須是錯誤的。 – arenaq

+1

@arenaq double有53位尾數,只能精確到15-17位。有沒有辦法區分9223372036854775809.9和9223372036854775807 http://stackoverflow.com/q/588004/995714 –

1

當你施放一個浮點類型的intlong,其結果要麼是最接近的整數(朝四捨五入零),或MIN_VALUEMAX_VALUEintlong。見JLS 5.1.3

因此,一種替代方法是做類型轉換,然後測試合適的MIN_VALUEMAX_VALUE

請注意,Long.MAX_VALUE9223372036854775807 ...這是您的測試程序輸出的數字!

(不過,如果你是鑄造一個浮點類型bytecharshort這是行不通的。見上面說明的鏈接。)