Pourquoi 0x80000000L est positif

Je pensais que 0x80000000L était négatif et que 0x80000000UL était positif.
Le programme en dessous me retourne positif.
Je fais une erreur grossière?

void setup()
{
  Serial.begin(115200);

  if (0x80000000L >= 0) Serial.println("Positif");
  else Serial.println("Négatif");
}

void loop(){}

Bonjour,
J’avais posté une ânerie que je supprime… une façon de tester le nouveau forum.
Bonne journée,
MicroQuettas

Bonjour,

Oui, c’est étrange. 0x80000000L est interprété comme unsigned long. C’est peut être normal car si on veut un long négatif, on utilise un nombre négatif.
Par contre si tu castes en long ça fonctionne

  if ((long)0x80000000L >= 0) Serial.println("Positif");
  else Serial.println("Négatif");

8 digits hexa ça fait 32 bits, et un long en Arduino c’est sur 64 bits me semble t’il. Donc en réalité la déclaration est équivalente à 0x000000008000000 ce qui est positif.

faut faire un Serial.println ( sizeof(long) ).
J’ai la flemme.

si vous faites

void setup()
{
  Serial.begin(115200);
  long x = 0x80000000L;

  if (x >= 0) Serial.println("Positif");
  else Serial.println("Négatif");
}

void loop() {}

je suis prêt à parier que vous serez Négatif

c’est la règle des constantes littérales : le compilateur quand il voit le suffixe L va affecter donc le type suivant cet ordre (pour que ça rentre)

  • long int
  • unsigned long int
  • long long int (depuis C++11)
  • unsigned long long int (depuis C++11)

dans votre cas puisque il pense que votre nombre doit être positif ça ne rentre pas, le compilateur allouera un type unsigned long int et donc vous serez positif

Non c’est sur 32 bits

D’où l’intérêt de ne pas utiliser ces types ambigus qui sont plateforme dépendants et de préférer intx_t ou uintx_t, avec x=8,16,32,64 au moins on sait de quoi on parle.

1 Like

Mais pour moi 0x80000000 est le plus petit entier négatif. Celui ci je sais l’écrire sans calculatrice. alors qu’en décimal c’est une autre paire de manche!

Si je passe par une variable tout rentre dans l’ordre, car le cast est automatique.

Des fois je me demande à quoi cela sert de préciser L ou UL!

C’est des fois un peu le problème que j’ai: je vais supposer ce que doit penser le compilateur pour changer mon écriture.

Ben justement, je n’ai pas employé de type du tout! Maintenant comme dans mon code il y a plein de choses qui sont soit Uno-dépendant soit Mega-dépendant, les codes que j’écris dépendent fortement de la carte utilisée. Avec une Uno, j’ai tendence à utiliser le plus possible le type byte parce que la mémoire est comptée, alors que si je programmais sur PC, j’utiliserai plus le type int (32 bits).

Conclusion: Pour avoir le plus petit entier sur 4 octets, je prendrais a calculatrice et pas ma Uno. Le cas montré ne peut pas vraiment exister dans un vrai programme sauf si on fait:

long long monNombre;
...
munNombre = 0x8000000L;  // Au lieu de  munNombre = -2147483648;

euh… pas supposer
→ il ne s’agit pas de supposer, le compilateur fait dans ce cas ce que dit la norme… si ça ne correspond pas à votre attente, c’est un bug du programmeur :wink: pas du compilateur… faut lire la spec…

Des fois je me demande à quoi cela sert de préciser L ou UL!

Quand vous écrivez un entier littéral dans une opération si rien n’est précisé (EDIT: et qu’il tient sur un int - merci @vileroi) alors il est représenté par un int et si le reste des éléments sont des int aussi, alors le calcul se fait en int, soit sur 16 bits sur un UNO.

Cas classique

const unsigned long uneHeure = 60 * 60 * 1000; // 1 heure en milli-secondes

void setup() {
  Serial.begin(115200);
  Serial.println(uneHeure); // 1 heure en ms = 3 600 000
}

void loop() {}

vous pensez voir 3600000 mais non c’est 4294962816 qui va s’afficher (sur un uno. sur un processeur 32 bits les int sont assez grand et donc il n’y a pas de problème)

(si vous avez les options de warning, le compilo vous prévient warning: integer overflow in expression)

Cependant si vous mettez le premier élément (au moins) en UL
const unsigned long uneHeure = 60UL * 60 * 1000; // 1 heure en milli-secondes
alors grace à la promotion automatique au fur et à mesure du calcul de tous les autres entiers en UL, le calcul est correct et le moniteur série donne bien 3600000

bref il faut lire la spec quand on essaye de jouer un peu bas niveau pour être sûr de son coup ou que l’on ne tombe pas dans un cas “undefined behavior” de la spécification. Il faut aussi se souvenir que l’on est avec un compilateur C++ et que ce n’est pas exactement comme le C, il y a de petites différences ici et là.

1 Like

Effectivement dans le cas de l’heure, c’est nécessaire. Mais si je fais

unsigned long uneHeure = 3600000;

il n’y a pas besoin de spécifier UL

tout à fait, car le compilateur fait comme pour votre 0x80000000, pour être plus précis ce n’est pas toujours int, c’est le premier truc que le compilateur essaye : il recherchera le plus petit storage container compatible avec la valeur donnée, donc ici 3600000 ne tient pas dans un int, pas dans un unsigned int mais tient dans un long int, ce sera donc le type choisi.

Puis ce long int est affecté dans une variable de type unsigned long, c’est un changement de type autorisé et la norme définit ce qu’il doit se passer → si la valeur d’origine est représentable dans le type de résultat, la valeur est inchangée