2015-09-08 10 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.)

Powiązane problemy