2015-09-08 11 views
7

Penso che la domanda sia abbastanza chiara. ma ecco un esempio.Come testare se il valore memorizzato in doppio adattamento è lungo? (arrotondando sì, ma non troncando)

L'esempio di seguito è OK. Posso fare l'arrotondamento e non è stato possibile troncare qui.

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

uscita:

9 

E ora il numero di serie di lungo:

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

uscita:

9223372036854775807 

Questo mi turba. Non posso continuare a lavorare con un numero completamente diverso. Preferirei ricevere un errore o un'eccezione.

C'è un modo per rilevare questo in Java?

risposta

12

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 
+0

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

+0

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

+1

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

1

Quando lanci un tipo virgola mobile in un int o long, il risultato è il numero intero più vicino (arrotondamento verso zero) o MIN_VALUE o MAX_VALUE per int o long. Vedi JLS 5.1.3.

Quindi, un'alternativa sarebbe quella di fare il typecast e quindi testare l'appropriato MIN_VALUE o MAX_VALUE.

Nota che Long.MAX_VALUE è 9223372036854775807 ... che è il numero che il tuo programma di test emette!

(Tuttavia, questo non funziona se si sta lanciando un tipo in virgola mobile a byte, char o short. Vedere il link qui sopra per la spiegazione.)

Problemi correlati