La limite de int
est comprise entre -2147483648 et 2147483647.
Si je saisis
int i = 2147483648;
alors Eclipse demandera un soulignement rouge sous "2147483648".
Mais si je fais ça:
int i = 1024 * 1024 * 1024 * 1024;
ça va compiler bien.
public class Test {
public static void main(String[] args) {
int i = 2147483648; // error
int j = 1024 * 1024 * 1024 * 1024; // no error
}
}
C'est peut-être une question fondamentale en Java, mais je ne vois pas pourquoi la deuxième variante ne produit aucune erreur.
Il n'y a rien de mal à cette déclaration; vous ne faites que multiplier 4 nombres et l'assigner à un int, il se trouve qu'il y a un débordement. Cela diffère de l'attribution d'un seul littéral, qui serait vérifié par les bornes au moment de la compilation.
C'est le hors-jeu littéral qui cause l'erreur, pas le affectation:
System.out.println(2147483648); // error
System.out.println(2147483647 + 1); // no error
En revanche, un littéral long
compilerait bien:
System.out.println(2147483648L); // no error
Notez que, en fait, le résultat est est toujours calculé au moment de la compilation car 1024 * 1024 * 1024 * 1024
est un expression constante :
int i = 1024 * 1024 * 1024 * 1024;
devient:
0: iconst_0
1: istore_1
Notez que le résultat (0
) est simplement chargé et stocké et aucune multiplication n’a lieu.
De JLS §3.10.1 (merci à @ChrisK pour l'avoir signalé dans les commentaires):
Il s'agit d'une erreur de compilation si un littéral décimal de type
int
est supérieur à2147483648
(231), ou si le littéral décimal2147483648
apparaît n'importe où autrement que comme l'opérande de l'opérateur unaire moins ( §15.15.4 ).
1024 * 1024 * 1024 * 1024
et 2147483648
n'ont pas la même valeur en Java.
Réellement, 2147483648
N'EST PAS MÊME UNE VALEUR (bien que 2147483648L
_ est) en Java. Le compilateur ne sait littéralement pas ce que c'est ni comment l'utiliser. Alors ça pleure.
1024
est un int valide en Java, et un int
valide multiplié par un autre int
valide, est toujours un int
valide. Même si ce n'est pas la même valeur que ce à quoi vous vous attendriez intuitivement car le calcul débordera.
Prenons l'exemple de code suivant:
public static void main(String[] args) {
int a = 1024;
int b = a * a * a * a;
}
Vous attendriez-vous à ce que cela génère une erreur de compilation? Cela devient un peu plus glissant maintenant.
Et si on mettait une boucle avec 3 itérations et qu'on la multipliait?
Le compilateur est autorisé à optimiser, mais il ne peut pas changer le comportement du programme en le faisant.
Quelques informations sur la manière dont cette affaire est réellement traitée:
Dans Java et de nombreux autres langages, les entiers consisteront en un nombre fixe de bits. Les calculs qui ne rentrent pas dans le nombre de bits donné vont débordement ; le calcul est fondamentalement exécuté module 2 ^ 32 en Java, après quoi la valeur est reconvertie en un signé entier.
D'autres langages ou API utilisent un nombre de bits dynamique (BigInteger
en Java), déclenchent une exception ou définissent la valeur sur une valeur magique telle que not-a-number.
Je ne sais pas pourquoi la deuxième variante ne produit aucune erreur.
Le comportement que vous suggérez - c'est-à-dire la production d'un message de diagnostic lorsqu'un calcul produit une valeur supérieure à la plus grande valeur pouvant être stockée dans un entier - est un caractéristique. Pour que vous puissiez utiliser n'importe quelle fonctionnalité, celle-ci doit être pensée, considérée comme une bonne idée, conçue, spécifiée, mise en œuvre, testée, documentée et envoyée aux utilisateurs.
Pour Java, un ou plusieurs éléments de cette liste ne se sont pas produits et vous ne disposez donc pas de cette fonctionnalité. Je ne sais pas lequel vous auriez à demander à un Java designer.
Pour C #, tout cela est arrivé - il y a environ quatorze ans maintenant - et le programme correspondant en C # a généré une erreur depuis le C # 1.0.
En plus de la réponse de arshajii, je souhaite montrer une dernière chose:
Ce n'est pas le assignation qui cause l'erreur mais simplement l'utilisation du littéral. Quand tu essaies
long i = 2147483648;
vous remarquerez que cela provoque également une erreur de compilation puisque le côté droit est toujours un int
- littéral et hors de portée.
Ainsi, les opérations avec int
- valeurs (y compris les affectations) risquent de déborder sans erreur de compilation (et également sans erreur d'exécution), mais le compilateur ne peut tout simplement pas gérer ces littéraux trop volumineux.
A: Parce que ce n'est pas une erreur.
Background: La multiplication 1024 * 1024 * 1024 * 1024
entraînera un débordement. Un débordement est très souvent un bug. Différents langages de programmation produisent un comportement différent en cas de débordement. Par exemple, C et C++ appellent cela "comportement indéfini" pour les entiers signés et le comportement est défini comme un entier non signé (prenez le résultat mathématique, ajoutez UINT_MAX + 1
tant que le résultat est négatif, soustrayez UINT_MAX + 1
tant que le résultat est supérieur à UINT_MAX
).
Dans le cas de Java, si le résultat d'une opération avec int
valeurs ne se situe pas dans la plage autorisée, conceptuellement Java ajoute ou soustrait 2 ^ 32 jusqu'à ce que le résultat soit dans la Ainsi, la déclaration est tout à fait légale et non erronée, elle ne produit tout simplement pas le résultat escompté.
Vous pouvez certainement vous demander si ce comportement est utile et si le compilateur doit vous avertir. Personnellement, je dirais qu'un avertissement serait très utile, mais une erreur serait inexacte puisqu'il s'agit de Java légal.