2012-05-17 14 views
7

Stavo cercando di rintracciare un comportamento molto strano di Java. Ho una formula che comporta un doppio, ma è "garantita" per dare una risposta intera - in particolare, un numero intero a 32 bit senza segno (che, purtroppo, Java non funziona bene). Sfortunatamente, le mie risposte erano a volte errate.I cast primitivi di tipo intero Java sono "limitati" al MAX_INT del tipo di trasmissione?

Alla fine ho trovato il problema, ma il comportamento è ancora molto strano per me: un double getto direttamente ad un int sembra essere limitato alla MAX_INT per un intero con segno, mentre un cast double ad un long che è quindi il cast in un int mi dà la risposta attesa (-1; il MAX INT di un numero intero a 32 bit senza segno rappresentato come un intero con segno a 32 bit).

ho scritto un piccolo programma di test:

public static void main(String[] args) { 
    // This is the Max Int for a 32-bit unsigned integer 
    double maxUIntAsDouble = 4294967295.00; 
    long maxUintFromDoubleAsLong = (long)maxUIntAsDouble; 
    long maxUintFromDoubleAsInt = (int)maxUIntAsDouble; 
    int formulaTest = (int) (maxUintFromDoubleAsLong * 1.0); 
    int testFormulaeWithDoubleCast = (int)((long) (maxUintFromDoubleAsLong * 1.0)); 
    // This is a more-or-less random "big number" 
    long longUnderTest = 4123456789L; 
    // Max int for a 32-bit unsigned integer 
    long longUnderTest2 = 4294967295L; 
    int intFromLong = (int) longUnderTest; 
    int intFromLong2 = (int) longUnderTest2; 
    System.out.println("Long is: " + longUnderTest); 
    System.out.println("Translated to Int is:" + intFromLong); 
    System.out.println("Long 2 is: " + longUnderTest2); 
    System.out.println("Translated to Int is:" + intFromLong2); 
    System.out.println("Max UInt as Double: " + maxUIntAsDouble); 
    System.out.println("Max UInt from Double to Long: " + maxUintFromDoubleAsLong); 
    System.out.println("Max UInt from Double to Int: " + maxUintFromDoubleAsInt); 
    System.out.println("Formula test: " + formulaTest); 
    System.out.println("Formula Test with Double Cast: " + testFormulaeWithDoubleCast); 
} 

Quando ho eseguito questo piccolo programma ottengo:

Long is: 4123456789 
Translated to Int is:-171510507 
Long 2 is: 4294967295 
Translated to Int is:-1 
Max UInt as Double: 4.294967295E9 
Max UInt from Double to Long: 4294967295 
Max UInt from Double to Int: 2147483647 
// MAX INT for an unsigned int 
Formula test: 2147483647 
// Binary: all 1s, which is what I expected 
Formula Test with Double Cast: -1 

Le due linee di fondo sono quelli che sto cercando di capire. Il doppio cast mi dà il previsto "-1"; ma il cast diretto mi dà MAX_INT per un intero con segno a 32 bit. Venendo da uno sfondo C++, capirei se mi dava un "numero dispari" invece del previsto -1 (ovvero "ingenuo casting"), ma questo mi lascia perplesso.

Così, alla domanda, allora: è questo comportamento "previsto" in Java (per esempio ogni double getto direttamente a un int sarà "limitato" per MAX_INT)? Il cast esegue questo per qualsiasi tipo imprevisto? Mi aspetto che sia simile per short e byte, per esempio; ma qual è il "comportamento previsto" quando si esegue il casting di un oversize-double?

Grazie!

risposta

10

Questo è il comportamento previsto. Ricorda che non ci sono primitive unsigned long o int types in Java e lo Java Language Specification (Java 7) per la riduzione della conversione primitiva (5.1.3) afferma che il lancio di un valore in virgola mobile "troppo piccolo o troppo grande" (sia doppio che float) a un tipo integrale di int o long utilizzerà il valore minimo o massimo di tipi integrali firmati (enfasi mia):

una conversione ambito di un numero a virgola mobile a un tipo integrale T assume due fasi:

  1. Nella prima fase, il numero in virgola mobile è convertita in lungo, se T è long, o to int, se T è byte, short, char o int, come segue:

    • Se il numero a virgola mobile viene NaN (§4.2.3), il risultato della prima fase della conversione è un int long 0.
    • Altrimenti, se il numero a virgola mobile non è un infinito, il valore in virgola mobile viene arrotondato a un valore intero V, arrotondando lo zero utilizzando la modalità IEEE 754 round-zero (§4.2.3). Poi ci sono in due casi:

      • a. Se T è lungo e questo valore intero può essere rappresentato come un lungo, il risultato del primo passo è il valore lungo V.
      • b. Altrimenti, se questo valore intero può essere rappresentato come un int, allora il risultato della prima fase è il valore int V.
    • Altrimenti, uno dei due casi seguenti devono essere vere:

      • a. Il valore deve essere troppo piccolo (un valore negativo di grande magnitudine o infinito negativo) e il risultato del primo passo è il valore rappresentativo minimo di tipo int o long.
      • b. Il valore deve essere troppo grande (un valore positivo di grande magnitudine o infinito positivo) e il risultato del primo passo è il valore rappresentabile massimo di tipo int o long. *
  2. Nella seconda fase: * Se T è int o lunga, il risultato della conversione è il risultato della prima fase. * Se T è byte, char o breve, il risultato della conversione è il risultato di una restringimento della conversione al tipo T (§5.1.3) del risultato del primo passaggio.

Esempio 5.1.3-1. Ambito conversione Primitive

class Test { 
    public static void main(String[] args) { 
     float fmin = Float.NEGATIVE_INFINITY; 
     float fmax = Float.POSITIVE_INFINITY; 
     System.out.println("long: " + (long)fmin + ".." + (long)fmax); 
     System.out.println("int: " + (int)fmin + ".." + (int)fmax); 
     System.out.println("short: " + (short)fmin + ".." + (short)fmax); 
     System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); 
     System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); 
    } 
} 

Questo programma produce l'output:

long: -9223372036854775808..9223372036854775807 
int: -2147483648..2147483647 
short: 0..-1 
char: 0..65535 
byte: 0..-1 

I risultati per char, int e long sono sorprendente, producendo valori massimi rappresentabili del tipo del minimo e.

I risultati per byte e cortocircuito perdono le informazioni sul segno e la magnitudo dei valori numerici e perdono anche la precisione.I risultati possono essere compresi esaminando i bit di ordine basso del minimo e int max. L'int minimo è, in esadecimale, 0x80000000 e l'int massimo è 0x7fffffff. Questo spiega i risultati brevi, che sono i 16 bit bassi di questi valori, ovvero 0x0000 e 0xffff; it spiega i risultati char, che sono anche i 16 bit bassi di questi valori , ovvero "\ u0000" e "\ uffff"; e spiega i risultati del byte , che sono gli 8 bit bassi di questi valori, ovvero 0x00 e 0xff.

Il primo caso int formulaTest = (int) (maxUintFromDoubleAsLong * 1.0); promuove così maxUintFromDoubleAsLong in doppio tramite moltiplicazione e poi l'inserisce in un int. Poiché il valore è troppo grande per rappresentare un numero intero con segno, il valore diventa 2147483647 (Integer.MAX_VALUE) o 0x7FFFFFFF.

quanto riguarda quest'ultimo caso:

Una conversione restringimento di un intero con segno a un tipo integrale T scarta semplicemente tutti ma i bit di ordine n bassi, dove n è il numero di bit utilizzati per rappresentare tipo T. Oltre ad una possibile perdita di informazioni sulla grandezza del valore numerico, questo potrebbe causare il segno del valore risultante per differire dal segno del valore di input .

Così int testFormulaeWithDoubleCast = (int)((long) (maxUintFromDoubleAsLong * 1.0)); prima promuove maxUintFromDoubleAsLong di raddoppiare, torna a lungo (ancora montaggio) e poi a un int. Nell'ultimo cast, i bit in eccesso vengono semplicemente eliminati, lasciandoti con 0xFFFFFFFF, che è -1 quando interpretato come un intero con segno.

+0

Sapevo della mancanza di "unsigned" in java (una delle cose che rende Java un tale problema quando si lavora con i protocolli bit-oriented), ma la maggior parte del resto è nuova di zecca. Sicuramente non mi aspettavo questo comportamento da "short" e "byte". Grazie. : D –

3

Questo è il modo in cui è scritta la specifica della lingua. Convertire un punto variabile in un tipo intero, se il valore è troppo grande per la destinazione, allora il valore massimo viene sostituito. In una conversione restringente da un tipo intero a uno più piccolo, i bit di ordine superiore vengono scartati.

Vedi l'JLS 5.1.3. Narrowing Primitive Conversion

Quindi, la risposta alla domanda nel titolo è "sì".

Problemi correlati