si può confrontare con Long.MIN_VALUE
e Long.MAX_VALUE
:
public static boolean fitsLong(double d) {
return d >= Long.MIN_VALUE && d < Long.MAX_VALUE;
}
approccio un po 'più sofisticato è quello di utilizzare 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
In questo modo è possibile controllare come viene eseguito l'arrotondamento.
Si può chiedere, perché c'è una rigorosa disuguaglianza in fitsLong
: d < Long.MAX_VALUE
. In realtà è perché lo stesso Long.MAX_VALUE
non può essere rappresentato come doppio numero. Quando lanci (double)Long.MAX_VALUE
, non c'è abbastanza precisione nel tipo double
per rappresentarlo, quindi viene selezionato il valore rappresentativo più vicino che è 9223372036854775808.0
(Long_MAX_VALUE+1.0
). Quindi era d <= Long.MAX_VALUE
restituire true
per il numero che è in realtà un po 'più grande come in questo confronto Long.MAX_VALUE
costante è promosso a doppio tipo. D'altra parte Long.MIN_VALUE
può essere esattamente rappresentato nel tipo double
, quindi qui abbiamo >=
.
Inoltre è interessante perché le seguenti opere:
double value = -9223372036854775809.9; // Long.MIN_VALUE-1.9
System.out.println(fitsLong(value)); // returns true
Ecco perché in realtà non hai sottrarre nulla dalla Long.MIN_VALUE
. Vedere:
double d1 = Long.MIN_VALUE;
double d2 = -9223372036854775809.9;
System.out.println(d1 == d2); // true
La doppia precisione non è sufficiente per distinguere tra -9223372036854775808
e -9223372036854775809.9
, quindi è in realtà lo stesso numero doppio. Durante la compilazione è convertito in forma binaria, e il modulo binario per questi due numeri è lo stesso. Quindi avendo un programma compilato non è possibile distinguere se -9223372036854775808
o -9223372036854775809.9
era nel codice sorgente.
Se si ritiene che è ancora il problema, costruire il BigDecimal
dal 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
Per la prima soluzione) In realtà non si può. L'ho provato e non funzionerà. Ad esempio, prova un valore 9223372036854775809.9 che viene incrementato di Long.MAX_VALUE per 2.9. Passerà. – arenaq
Hai ragione, ma quando uso 9223372036854775807.0 per entrambe le soluzioni. FitsLong mi dà true e BigDecimal lancia un'eccezione. Quel doppio valore può essere invariato, ma almeno una delle soluzioni deve essere sbagliata. – arenaq
@arenaq double ha 53 bit di mantissa e può essere preciso solo a ~ 15-17 cifre. Non c'è modo per distinguere tra 9223372036854775809.9 e 9223372036854775807 http://stackoverflow.com/q/588004/995714 –