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
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
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
@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 –