[RÉSOLU] Unsigned Long calcul érroné : il faut mettre (unsigned long) devant

Bonjourà tous

J'ai rencontré un soucis de calcul avec des variables uint16_t. Voici :

[code// ========= test 1 ========= //
uint32_t diviseur = 0;
uint16_t maxiTAB[ 7] = { 65535, 365, 24, 60, 60, 1000, 1000 };

// ceci renvoi une valeur erronée
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6];
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6] * 1UL;
diviseur = 60 * 60 * 1000 * 1000;
diviseur = 60 * 60 * 1000 * 1000UL;

// ========= test 2 ========= //
uint32_t diviseur = 0;
uint16_t maxiTAB[ 7] = { 65535UL, 365UL, 24UL, 60UL, 60UL, 1000UL, 1000UL };   // rajout de UL

// ceci renvoi une valeur erronée
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6];
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6] * 1UL;

// ceci renvoi une valeur juste (3 600 000 000)

diviseur = 60UL * 60UL * 1000L * 1000UL;    // diviseur = 3 600 000 000
// ou
diviseur = 1;              // diviseur = 1
diviseur *= 60;            // diviseur = 60
diviseur *= 60;            // diviseur = 3 600
diviseur *= 1000;          // diviseur = 3 600 000
diviseur *= 1000;          // diviseur = 3 600 000 000
// ou
diviseur = 1;              // diviseur = 1
diviseur *= maxiTAB[3];    // diviseur = 60
diviseur *= maxiTAB[4];    // diviseur = 3 600
diviseur *= maxiTAB[5];    // diviseur = 3 600 000
diviseur *= maxiTAB[6];    // diviseur = 3 600 000 000

Auriez-vous quelques directives ou conseils à me communiquer pour résoudre ce soucis de calcul ?
[edit 1]
diviseur est en uint32_t pas en uint16_t

// ceci renvoi une valeur erronée
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6];
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6] * 1UL;

tu as l'erreur avec les 2 tests? même avec les constantes suffixées avec UL?

fdufnews:
tu as l'erreur avec les 2 tests? même avec les constantes suffixées avec UL?

fdufnews : oui, même avec les constantes suffixées en UL dans le tableau

Et si tu commences par 1ul au lieu de terminer ?

diviseur doit être en ‘unsigned long’ ou ‘uint32_t’.
uint16_t, c’est trop petit.

bilbo83 a raison le résultat du calcul est tronqué car il dépasse 65536. Du coup tu récupères une valeur modulo 65536.

hello :slight_smile:

grillé par les copains :slight_smile:

Serial.begin(115200);
uint32_t diviseur = 0;
uint32_t maxiTAB[ 7] = { 65535UL, 365UL, 24UL, 60UL, 60UL, 1000UL, 1000UL };   // rajout de UL

// ceci renvoi une valeur erronée

diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6] * 1UL;
Serial.println(diviseur);
[quote][quote author=dfgh link=msg=4142927 date=1555663512]
h
uint32_t maxiTAB[ 7] = { 65535[color=red]UL[/color], 365[color=red]UL[/color], 24[color=red]UL[/color], 60[color=red]UL[/color], 60[color=red]UL[/color], 1000[color=red]UL[/color], 1000[color=red]UL[/color] };   // rajout de UL

Serial.println(diviseur);

[/quote]

Pas besoin de UL dans les initialisations

bilbo83:
diviseur doit être en 'unsigned long' ou 'uint32_t'.
uint16_t, c'est trop petit.

très juste ! je n'ai pas le programme sous les yeux (je suis au taff, oui je sais c'est pas bien...)
Mais de mémoire oui, je sais que je suis en unsigned long (je viens de corriger mon post initial)
sinon uint16_t diviseur = 60UL60UL1000UL*1000UL; n'aurait jamais été juste

autre question, est-il nécessaire que
diviseur soit en uint32_t : OUI nécessairement (mais je sais que j'y suis)
maxiTAB[7] soit en uint32_t aussi ? : aucune variable dedans ne dépasse 65535.

dfgh:

uint32_t diviseur = 0;

uint32_t maxiTAB[ 7] = { 65535UL, 365UL, 24UL, 60UL, 60UL, 1000UL, 1000UL };   // rajout de UL

Pas besoin de UL pour les initialisations

On ajoutera UL (ou ul) que si on fait des calculs pour forcer le type.

ça c'est bon par exempleunsigned long uneHeure = 3600000; // nombre de ms dans 1hmais ça ne marcherait pas si vous écrivez celaunsigned long uneHeure = 60 * 60 * 1000; // nombre de ms dans 1h (ERREUR)car le pré-processeur va effecteur le calcul des multiplication en nombre entier, il faut écrire dans ce casunsigned long uneHeure = 60 * 60 * 1000UL; // nombre de ms dans 1h avec au moins un UL dans les opérations pour forcer tout le calcul en unsigned long.

En pratique ça ne mange pas de pain de mettre le UL partout, ça montre que l'on est conscient de ce que l'on fait et ça ne me gênerait absolument pas de voir unsigned long uneHeure = 3600000UL; // nombre de ms dans 1h

J-M-L:

unsigned long uneHeure = 60 * 60 * 1000UL; // nombre de ms dans 1h

avec au moins un UL dans les opérations pour forcer tout le calcul en unsigned long.

Je re-testerai mais, de mémoire, hier soir j'ai testé ceci :

unsigned long uneHeure = 60 * 60 * 1000UL; // renvoi une valeur erronée

mais cela renvoyait une valeur erronée

C'est ce que j'ai indiqué dans mon post initial :

// ceci renvoi une valeur erronée
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6];
diviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6] * 1UL;
diviseur = 60 * 60 * 1000 * 1000;
diviseur = 60 * 60 * 1000 * 1000UL;

vous devriez tester...

unsigned long uneHeure = 60 * 60 * 1000UL;
void setup()
{
  Serial.begin(115200);
  Serial.println(uneHeure);
}

void loop() {}

moi je parie que la console affichera

3600000

Je testerai ce soir pour vérifier mais de mémoire ça ne marchait pas non plus.
Cette information (UL au moins 1 fois) m'a déjà été indiquée sur un groupe Facebook c'est exactement ce qui m'avait permis de soulever ce problème.

Hitsugaya81:
Je testerai ce soir pour vérifier mais de mémoire ça ne marchait pas non plus.
Cette information (UL au moins 1 fois) m’a déjà été indiquée sur un groupe Facebook c’est exactement ce qui m’avait permis de soulever ce problème.

Parceque diviseur était déclaré uint16_t (16 bits) et pas en unsigned long (32 bits).

Hitsugaya81:
autre question, est-il nécessaire que
diviseur soit en uint32_t : OUI nécessairement (mais je sais que j'y suis)
maxiTAB[7] soit en uint32_t aussi ? : aucune variable dedans ne dépasse 65535.

Si maxiTAB est déclaré en uint16_t les opérations comme celle-cidiviseur = maxiTAB[3] * maxiTAB[4] * maxiTAB[5] * maxiTAB[6];se feront sur ce type et donc il y aura un débordement.

bidouilleelec:
Parceque diviseur était déclaré uint16_t (16 bits) et pas en unsigned long (32 bits).

Oui, ça je sais, on me l'a déjà dit ici et j'ai précisé déjà plus haut que c'était une erreur d'écriture sur ce post et non pas une erreur de code hier soir (car là je suis au taff)
sinon uint16_t diviseur = 60UL60UL1000UL*1000UL; n'aurait jamais été juste
Alors que je sais que hier soir c'était juste (donc hier soir j'avais bien eu écrit "unsigned long diviseur = ...")

Donc le soucis n'est pas "uint16_t" ou "unint32_t" (car en faite c'était unsigned long, qui est équivalent à uint32_t)

fdufnews:
Si maxiTAB est déclaré en uint16_t les opérations se feront sur ce type et donc il y aura un débordement.

Voila qui répond mieux à ma question et expose l'origine du soucis.

Est-ce un bug du compilateur ?
Pourquoi faire les calculs en 16bits alors que la variable résultat est en 32 ?

Du coup, si diviseur = 60;
reste juste car l'opérande est "
="
ce qui équivaut à : diviseur = diviseur * 60.
Il y a donc une variable 32 bits dans l'opération
donc l'opération sera faite en 32 bits.
J'ai juste ?

Hitsugaya81:
Donc le soucis n'est pas "uint16_t" ou "unint32_t" (car en faite c'était unsigned long, qui est équivalent à uint32_t)

Au début tu indiquais uint16_t, puis uint32_t puis maintenant unsigned long. Tu as retranscrit le code de mémoire. Le mieux c'est que tu republies le code foireux original et comme ça on pourra parler sur une base solide.
Sinon dans une heure il y aura 100 posts sur le sujet sans aucune certitude quand à la cause.

fdufnews:
Au début tu indiquais uint16_t, puis uint32_t puis maintenant unsigned long. Tu as retranscrit le code de mémoire. Le mieux c'est que tu republies le code foireux original et comme ça on pourra parler sur une base solide.
Sinon dans une heure il y aura 100 posts sur le sujet sans aucune certitude quand à la cause.

oui, je cloturerai ou supprimerai ce post pour une meilleur pertinence.
cependant il me semblait que "uint32_t" ou "unsigned long" c'était la même chose, vrai ?

Hitsugaya81:
Est-ce un bug du compilateur ?
Pourquoi faire les calculs en 16bits alors que la variable résultat est en 32 ?

Non c'est la spécification de la norme qui veut cela. Le compilateur dans une affectation calcule d'abord ce qui est à droite du égal, puis ensuite fait la promotion / conversion de type nécessaire pour stocker le résultat dans la variable de destination (à gauche du égal). La norme dit que le calcul s'effectue avec le type "le plus grand" de tous les opérandes du calcul. donc si vous avez au moins un UL dans la formule, alors tout est fait en UL.

attention, le pré-processeur lui fait aussi ses calculs avant la compilation donc quand vous avez des valeurs numériques, par défaut (sans suffixe L, UL, ...) il considère que ce sont des int et donc sur un UNO ce serait calculé sur 16 bits mais sur un ESP en 32 bits

Hitsugaya81:
oui, je cloturerai ou supprimerai ce post pour une meilleur pertinence.

ne virez rien. vous pouvez marquer le fil comme 'résolu' en éditant le titre du premier post

Hitsugaya81:
cependant il me semblait que "uint32_t" ou "unsigned long" c'était la même chose, vrai ?

ça dépend des architectures. La norme dit que un unsigned long comprend au moins 32 bits mais si vous compiliez cela sur un OS qui a une architecture full 64 bits (notée "LP64") comme mac OS par exemple ou la plupart des Unix aujourd'hui alors les unsigned long ont 64 bits. Dans le monde Arduino il n'ya pas de processeur 64 bits pour le moment donc c'est bien 32 bits.

Quand on veut forcer le nombre de bits, on utilise la notation spécifique comme [b][color=red]u[/color]int[color=red]32[/color]_t[/b] qui sont dans la norme depuis C99/C++11 (définis dans cstdint)