2015-09-08 48 views
7

Myślę, że to pytanie jest całkiem proste. ale tutaj są przykłady.Jak sprawdzić, czy wartość przechowywana w podwójnym dopasowaniu w długim? (zaokrąglając tak, ale skracając nie)

Przykład poniżej jest OK. Mogę zaokrąglić i tutaj nie zrobiono żadnego obcięcia.

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

wyjściowa:

9 

A teraz numer poza zasięgiem długa:

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

wyjściowa:

9223372036854775807 

ten jeden mnie martwi. Nie mogę kontynuować pracy z zupełnie innym numerem. Wolę błąd lub wyjątek.

Czy istnieje sposób na wykrycie tego w Javie?

Odpowiedz

12

Można porównać go z Long.MIN_VALUE i Long.MAX_VALUE:

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

Nieco bardziej sofisticated podejściem jest użycie 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 

W ten sposób można kontrolować w jaki sposób zaokrąglania jest wykonywana.

Możesz zapytać, dlaczego istnieje ścisła nierówność w fitsLong: d < Long.MAX_VALUE. Właściwie to dlatego, że sama Long.MAX_VALUE nie może być reprezentowana jako podwójna liczba. Podczas rzutowania (double)Long.MAX_VALUE nie ma wystarczającej dokładności w typie double, aby go reprezentować, dlatego wybrana jest najbliższa możliwa do reprezentowania wartość, która jest 9223372036854775808.0 (Long_MAX_VALUE+1.0). Tak więc to byłby d <= Long.MAX_VALUE, który zwróciłby true dla liczby, która jest rzeczywiście nieco większa, ponieważ stała tego porównania jest promowana do podwójnego typu. Z drugiej strony Long.MIN_VALUE może być dokładnie reprezentowany w typie double, stąd tutaj mamy >=.

Również to ciekawe dlaczego następujące prace:

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

To dlatego, że właściwie nic nie z Long.MIN_VALUE odejmować. Zobacz:

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

Podwójna precyzja nie jest wystarczająco odróżnić -9223372036854775808 i -9223372036854775809.9, więc jest to rzeczywiście ta sama liczba podwójna. Podczas kompilacji jest konwertowany do postaci binarnej, a formularz binarny dla tych dwóch liczb jest taki sam. Zatem po skompilowaniu programu nie można rozróżnić, czy -9223372036854775808 lub -9223372036854775809.9 było w kodzie źródłowym.

Jeśli czujesz, że jest to nadal problem, skonstruować BigDecimal od String:

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

Dla pierwszego rozwiązania) Właściwie nie można. Próbowałem i to nie zadziała. Na przykład wypróbuj wartość 9223372036854775809.9, która jest zwiększana o Long.MAX_VALUE o 2,9. To przejdzie. – arenaq

+0

Masz rację, ale kiedy używam 9223372036854775807.0 dla obu rozwiązań. FitsLong daje mi prawdziwość, a BigDecimal rzuca wyjątek. Ta podwójna wartość może pozostać niezmieniona, ale przynajmniej jedno z rozwiązań musi być błędne. – arenaq

+1

@arenaq double ma 53 bity mantysy i może być precyzyjny tylko do ~ 15-17 cyfr. Nie ma sposobu na rozróżnienie między 9223372036854775809.9 i 9223372036854775807 http://stackoverflow.com/q/588004/995714 –

1

Kiedy rzucisz pływającą typu punkt do int lub long, wynik jest albo najbliższa liczba całkowita (zaokrąglenie w kierunku zero) lub MIN_VALUE lub. Zobacz JLS 5.1.3.

W związku z tym jedną z możliwości jest wykonanie typografu, a następnie przetestowanie odpowiedniego modelu MIN_VALUE lub MAX_VALUE.

Należy pamiętać, że Long.MAX_VALUE to 9223372036854775807 ... który jest numerem, który wyprowadza program testowy!

(jednak to nie zadziała, jeśli odlewania pływającą typu wskaż byte, char lub short. Patrz powyższy link do wyjaśnienia.)