Perché int i = 1024 * 1024 * 1024 * 1024 si compila senza errori?


152

Il limite intè compreso tra -2147483648 e 2147483647.

Se inserisco

int i = 2147483648;

quindi Eclipse richiederà una sottolineatura rossa in "2147483648".

Ma se lo faccio:

int i = 1024 * 1024 * 1024 * 1024;

compilerà bene.

public class Test {
    public static void main(String[] args) {        

        int i = 2147483648;                   // error
        int j = 1024 * 1024 * 1024 * 1024;    // no error

    }
}

Forse è una domanda di base in Java, ma non ho idea del perché la seconda variante non produca errori.


10
Anche se il compilatore normalmente "comprime" il calcolo in un singolo valore come ottimizzazione, non lo farebbe se il risultato fosse un overflow, poiché nessuna ottimizzazione dovrebbe modificare il comportamento del programma.
Hot Licks

1
E non può interpretare 2147483648: questo letterale non ha senso.
Denys Séguret,

1
E Java non segnala overflow di numeri interi - l'operazione "fallisce" in silenzio.
Hot Licks

5
@JacobKrall: C # segnalerà questo come un difetto indipendentemente dal fatto che il check-ness sia attivato; tutti i calcoli costituiti solo da espressioni costanti vengono controllati automaticamente a meno che all'interno di un'area non selezionata.
Eric Lippert,

54
Ti scoraggio dal porre domande "perché no" su StackOverflow; sono difficili da rispondere. Una domanda "perché no" presuppone che il mondo dovrebbe ovviamente essere un modo in cui non lo è, e che ci deve essere una buona ragione perché sia ​​così. Questo presupposto non è quasi mai valido. Una domanda più precisa potrebbe essere qualcosa del tipo "quale sezione della specifica descrive come viene calcolata l'aritmetica di numeri interi costanti?" o "come vengono gestiti gli overflow dei numeri interi in Java?"
Eric Lippert,

Risposte:


233

Non c'è niente di sbagliato in questa affermazione; stai solo moltiplicando 4 numeri e assegnandoli a un int, c'è semplicemente un overflow. Ciò è diverso dall'assegnare un singolo valore letterale , che verrebbe controllato al limite in fase di compilazione.

È il letterale fuori limite che causa l'errore, non l' assegnazione :

System.out.println(2147483648);        // error
System.out.println(2147483647 + 1);    // no error

Al contrario un longletterale compilerebbe bene:

System.out.println(2147483648L);       // no error

Si noti che, in effetti, il risultato viene comunque calcolato in fase di compilazione perché 1024 * 1024 * 1024 * 1024è un'espressione costante :

int i = 1024 * 1024 * 1024 * 1024;

diventa:

   0: iconst_0      
   1: istore_1      

Si noti che il risultato ( 0) viene semplicemente caricato e memorizzato e non si verifica alcuna moltiplicazione.


Da JLS §3.10.1 (grazie a @ChrisK per averlo riportato nei commenti):

È un errore in fase di compilazione se un valore letterale decimale di tipo intè maggiore di 2147483648(2 31 ) o se il valore letterale decimale 2147483648appare in qualsiasi luogo diverso dall'operando dell'operatore meno unario ( §15.15.4 ).


12
E per la moltiplicazione JLS dice: Se trabocca una moltiplicazione intera, il risultato sono i bit di ordine inferiore del prodotto matematico rappresentati in un formato sufficientemente grande del complemento a due. Di conseguenza, se si verifica un overflow, il segno del risultato potrebbe non essere uguale al segno del prodotto matematico dei due valori di operando.
Chris K,

3
Risposta eccellente. Alcune persone sembrano avere l'impressione che l'overflow sia una sorta di errore o fallimento, ma non lo è.
Wouter Lievens,

3
@ iowatiger08 La semantica del linguaggio è delineata da JLS, che è indipendente dalla JVM (quindi non dovrebbe importare quale JVM usi).
Arshajii,

4
@WouterLievens, l'overflow è normalmente una condizione "insolita", se non addirittura una condizione di errore. È il risultato della matematica a precisione finita, che la maggior parte delle persone non si aspetta intuitivamente che accada quando fanno la matematica. In alcuni casi, come -1 + 1, è innocuo; ma 1024^4perché potrebbe accecare le persone con risultati totalmente inaspettati, lontani da ciò che si aspetterebbero di vedere. Penso che ci dovrebbe essere almeno un avvertimento o una nota per l'utente e non ignorarlo silenziosamente.
Phil Perry,

1
@ iowatiger08: la dimensione di int è fissa; essa non dipende dalla JVM. Java non è C.
Martin Schröder,

43

1024 * 1024 * 1024 * 1024e 2147483648non hanno lo stesso valore in Java.

In realtà, 2147483648 NON È ANCHE UN VALORE (anche se lo 2147483648Lè) in Java. Il compilatore non sa letteralmente di cosa si tratta o come utilizzarlo. Quindi si lamenta.

1024è un int valido in Java e un valido intmoltiplicato per un altro valido intè sempre valido int. Anche se non è lo stesso valore che ti aspetteresti intuitivamente perché il calcolo trabocca.

Esempio

Si consideri il seguente esempio di codice:

public static void main(String[] args) {
    int a = 1024;
    int b = a * a * a * a;
}

Ti aspetti che ciò generi un errore di compilazione? Ora diventa un po 'più scivoloso.
E se mettessimo un loop con 3 iterazioni e moltiplicassimo nel loop?

Il compilatore può ottimizzare, ma non può modificare il comportamento del programma mentre lo fa.


Alcune informazioni su come viene gestito questo caso:

In Java e in molte altre lingue, i numeri interi saranno costituiti da un numero fisso di bit. I calcoli che non rientrano nel dato numero di bit verranno traboccati ; il calcolo viene effettuato fondamentalmente modulo 2 ^ 32 in Java, dopo che il valore è tornato convertito in un firmata intero.

Altre lingue o API usano un numero dinamico di bit ( BigIntegerin Java), generano un'eccezione o impostano il valore su un valore magico come non-un-numero.


8
Per me, la tua affermazione, " 2147483648NON È ANCHE UN VALORE (anche se lo 2147483648Lè)", ha davvero rafforzato il punto che @arshajii stava cercando di fare.
kdbanman,

Ah, scusa, sì, ero io. Mi mancava la nozione di overflow / aritmetica modulare nella tua risposta. Nota che puoi tornare indietro se non sei d'accordo con la mia modifica.
Maarten Bodewes,

@owlstead La tua modifica è effettivamente corretta. La mia ragione per non includerlo è stata questa: indipendentemente da come 1024 * 1024 * 1024 * 1024viene gestita, volevo davvero sottolineare che non è la stessa cosa della scrittura 2147473648. Esistono molti modi (e ne hai elencati alcuni) che una lingua potrebbe potenzialmente gestirla. È ragionevolmente separato e utile. Quindi lo lascerò. Molte informazioni diventano sempre più necessarie quando si ha una risposta di alto livello su una domanda popolare.
Cruncher,

16

Non ho idea del perché la seconda variante non produca errori.

Il comportamento suggerito, ovvero la produzione di un messaggio diagnostico quando un calcolo produce un valore maggiore del valore più grande che può essere archiviato in un numero intero , è una funzione . Per poter utilizzare qualsiasi funzione, la funzione deve essere considerata, considerata una buona idea, progettata, specificata, implementata, testata, documentata e spedita agli utenti.

Per Java, una o più delle cose in quella lista non sono successe e quindi non hai la funzione. Non so quale; dovresti chiedere a un designer Java.

Per C #, tutte queste cose sono successe - circa quattordici anni fa ormai - e quindi il programma corrispondente in C # ha prodotto un errore da C # 1.0.


45
Questo non aggiunge nulla di utile. Anche se non mi dispiace prendere pugnalate a Java, non ha risposto alla domanda dei PO.
Seiyria,

29
@Seiyria: il poster originale chiede "perché no?" domanda - "perché il mondo non è come penso che dovrebbe essere?" non è una domanda tecnica precisa sul codice effettivo e questa è quindi una cattiva domanda per StackOverflow. Il fatto che la risposta corretta a una domanda vaga e non tecnica sia vaga e non tecnica non dovrebbe sorprendere. Incoraggio il poster originale a porre una domanda migliore ed evitare "perché no?" domande.
Eric Lippert,

18
@Seiyria: anche la risposta accettata che noto non risponde a questa domanda vaga e non tecnica; la domanda è "perché questo non è un errore?" e la risposta accettata è "perché è legale". Questo è semplicemente riaffermare la domanda ; rispondendo "perché il cielo non è verde?" con "perché è blu" non risponde alla domanda. Ma poiché la domanda è una cattiva domanda, non incolpo affatto il rispondente; la risposta è una risposta perfettamente ragionevole a una domanda scadente.
Eric Lippert,

13
Signor Eric, questa è la domanda che ho posto: "Perché int i = 1024 * 1024 * 1024 * 1024; senza segnalazione di errore nell'eclissi?". e la risposta di arshajii è esattamente ciò che io cosa (forse di più). A volte non posso esprimere alcuna domanda in modo molto preciso. Penso che sia per questo che alcune persone modificano alcune domande postate in modo più accurato in StackOverflow. Penso che se voglio ottenere la risposta "perché è legale", non posterò questa domanda. Farò del mio meglio per pubblicare alcune "domande regolari", ma per favore capisci qualcuno come me che è uno studente e non così professionale. Grazie.
WUJ,

5
@WUJ Questa risposta IMHO fornisce informazioni e prospettive aggiuntive. Dopo aver letto tutte le risposte ho trovato questa risposta per fornire la stessa validità delle altre risposte fornite. Inoltre aumenta la consapevolezza che gli sviluppatori non sono gli unici implementatori di alcuni prodotti software.
SoftwareCarpenter,

12

Oltre alla risposta di Arshajii, voglio mostrare un'altra cosa:

Non è il compito che causa l'errore ma semplicemente l'uso del letterale . Quando ci provi

long i = 2147483648;

noterai che provoca anche un errore di compilazione poiché il lato destro è ancora int-literale e fuori portata.

Quindi le operazioni con int-valori (e che includono le assegnazioni) possono traboccare senza un errore di compilazione (e senza un errore di runtime), ma il compilatore non può gestire quei letterali troppo grandi.


1
Destra. Assegnare un int a un long include un cast implicito. Ma il valore non può mai esistere come int in primo luogo per essere lanciato :)
Cruncher,

4

A: Perché non è un errore.

Sfondo: la moltiplicazione 1024 * 1024 * 1024 * 1024porterà ad un overflow. Un overflow è molto spesso un bug. Diversi linguaggi di programmazione producono comportamenti diversi quando si verificano overflow. Ad esempio, C e C ++ lo chiamano "comportamento indefinito" per numeri interi con segno, e il comportamento è definito numeri interi senza segno (prendere il risultato matematico, aggiungere UINT_MAX + 1finché il risultato è negativo, sottrarre UINT_MAX + 1finché il risultato è maggiore di UINT_MAX).

Nel caso di Java, se il risultato di un'operazione con intvalori non è compreso nell'intervallo consentito, concettualmente Java aggiunge o sottrae 2 ^ 32 fino a quando il risultato non rientra nell'intervallo consentito. Quindi la dichiarazione è completamente legale e non in errore. Semplicemente non produce il risultato che potresti aver sperato.

Puoi sicuramente discutere se questo comportamento è utile e se il compilatore dovrebbe darti un avvertimento. Direi personalmente che un avviso sarebbe molto utile, ma un errore sarebbe errato poiché è Java legale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.