Cast

Bonsoir à tous

J'ai une fonction:

float CalculLReserv(int ValRes, int Min, int Max, int L)
{
  return (L + ((float)ValRes - Min) / ((Max - Min)));
}

cette fonction est appelé comme ça:

    else if (ReservoirValue < 872 && ReservoirValue >= 776) { //3
      PrintTFT(135, 142, String(CalculLReserv(ReservoirValue, 776, 873 , 3)), 2);//Print L dans le reservoir
      PrintTFT(225, 142, String(CalculLReserv(ReservoirValue, 776, 873 , 3) * 25), 2);//Print Km 
    }

cette fonction me calcule les litres d’essence que j'ai dans mon réservoir.
L c'est un int entre 0 et 14.
ValRes est la valeur que lit l'arduino sur une entrée analogique de la jauge à essence.
Min et Max c'est le valeur que l'arduino lit entre un litre et l'autre .
ex:
0 litres j'ai une valeur de 980
1 litre 950
2 litres 900
3 litres 873 etc. etc.
ce n'est pas linéaire car un réservoir n'a pas une forme comme un parallélépipède.
donc

((float)ValRes - Min) / ((Max - Min))

me calcule approximativement le décimaux de litre.
ex: 1L min = 899 max = 950 ValRes = 920
La fonction "devrait" me retourner 1.41 L

Mes doutes vont sur la position de (float).
Vous pouvez me dire: " Essaye!!" mais j'aimerai si c'est possible un minimum de théorie sur ces cast .
J'ai lit ça et ça mais c'est très maigre.
aussi la différence entre
(float)nr
et float(nr)
Merci.

Bonsoir,

tu as besoin de faire un transtypage explicite pour forcer le calcul en nombre flottant pour la division. En effet en C, un entier divisé par un entier donne un entier (le quotient de la division). Idem avec les autres opérateurs (*, +, -, ...).

Par contre, lorsque le compilateur rencontre un entier divisé par un nombre flottant (ou vice versa) il transforme (temporairement) l'entier en flottant (transtypage implicite) puis fait la division qui est donc en nombre flottant, sans perte de la partie après la virgule (idem avec les autres opérateurs). Le résultat est un flottant.

Avec ces 2 règles et la connaissance de l'ordre et de la priorité des opérateurs (ici ce sont des opérateurs mathématiques donc c'est l'ordre et la priorité que l'on connait tous depuis le collège) ont peut savoir ce qui va être exécuté, et dans quel ordre, dans ton calcul :

L + ((float)ValRes - Min) / (Max - Min)

1 : float)ValRes - Min : float (grace au transtypage) - int -> float
2 : max-min : int - int -> int
3 : 1/2 : float / int -> float
4 : L + 3 : int + float -> float

Ta division est donc faite en nombre flottant, c'est ce que tu voulais je pense.

Ce qui n'aurait pas été le cas avec un transtypage mal placé genre :

(float) L + (ValRes - Min) / (Max - Min) (division entre 2 entiers).

Mais qui doit marcher avec :

L + (ValRes - Min) / ((float)Max - Min)
L + (float)(ValRes - Min) / (Max - Min)
...

Et pour les gros flemmarts :

L + (ValRes*1.0 - Min) / (Max - Min) # le compilateur n'a qu'a optimiser :wink:

Voila, en espérant que c'est ce genre d'explication que tu voulais...

EDIT :

aussi la différence entre
(float)nr
et float(nr)

(float)nr est un transtypage explicite. float(nr) est la création d'un float initialisé à nr. Ils peuvent être interchangé dans ton cas puisque float(unEntier) retourne le flottant créé.

Si le compilateur est bon, les 2 codes doivent produire un résultat aussi optimisé, dans ton cas.

Merci supercc.
Parfait, c'est ce que pensait.
Mes doutes viennent de ce Topic post #3

Dans la réponse de kamill, notez bien l'emplacement de (float) !!!!!
Directement devant la première variable.

"première" bien en rouge et soulignée.

(float)nr est un transtypage explicite. float(nr) est la création d'un float initialisé à nr. Ils peuvent être interchangé dans ton cas puisque float(unEntier) retourne le flottant créé.

Rien compris!! :confused:

Voyons d'expliquer ce que je comprends avec mes mot!
Si int nr;
(float)nr nr reste toujours un int il est considéré comme float temporairement.
float(nr) nr il est transformé en float pour le reste de sa vie.

C'est ça?

Dans tous les cs, nr reste toujours un entier (heureusement.... ça serait un terrible effet de bord).
JE crois que, pratiquement, les deux notations mênent à la même chose
float(nr):; crée une variable temporaire qui est le resultat de la conversion. Dieu merci, ça ne touche pas à nr.

(float)nr nr reste toujours un int il est considéré comme float temporairement.

Oui

float(nr) nr il est transformé en float pour le reste de sa vie.

Non. En C une variable ne peut pas vraiment changer de type. Elle restera toujours du type définit lors de sa déclaration. Ici c'est comme au dessus sauf que c'est une facilité du C++ (compile pas avec un compilateur C) qui est capable de créer des vars ou des objets de cette façon (variable/objet anonyme) avec appel du constructeur (bon dans le cas d'un float il n'y en a pas ;-)).

Donc sans rentrer dans les détails C++, ici cela revient au même.

Ici c'est comme au dessus sauf que c'est une facilité du C++

C'est aussi une facilité de fortran >= 66 (plus d'un demi siècle que l'on fait des conversions sans effets de bord....). Nota: je salue la volonté de C++ de copier , lentement, mais intelligemment, les fonctionnalités bien pratiques d'autres langages (orientés vers les utilisaturs, pas vers les Pogrammeurs Systèmes et autres Dieux).

J'ai résolu mon problème de cast!
Je pensais que le problème venais d'une partie du code mais au final était ailleurs.
Je reviens vers vous pour avoir confirmation de ce que je pense.
Vous avez un arduino tout neuf, vous téléchargez le code ou dans le setup il y a ces lignes

void setup(void)
{
....
  EEPROM.get(0, fOdo);
  fOdoBis = fOdo;
  EEPROM.get(100, fTrip);
  fTripBis = fTrip;

......

fOdo et fTrip sont déclaré comme float.
Est ce que à votre avis il peut y avoir un problème?

dbrion06:
C'est aussi une facilité de fortran >= 66 (plus d'un demi siècle que l'on fait des conversions sans effets de bord....). Nota: je salue la volonté de C++ de copier , lentement, mais intelligemment, les fonctionnalités bien pratiques d'autres langages (orientés vers les utilisaturs, pas vers les Pogrammeurs Systèmes et autres Dieux).

bon FORTRAN c’est 1954 - mais il a quand même fallu attendre la nome de 2008 pour avoir INT8, INT16, INT32 et INT64 et Les types prédéfinis en Fortran utilisant KIND c’est depuis la norme 90... (c’est un des premiers langages que j’ai appris)

Le cast ((float) monEntier) est sans effet de bord non plus en C depuis l’origine en 1972.

Le point sur lequel il voulait insister c’est que la notation float(monEntier) est sémantiquement différente du cast en C++

Je ne sais pas comment était Fortran 1954 (plus folklorique que 66; ça ne valait pas la peine de le standardiser...).
Je suis cependant sûr qu'il gérait aussi proprement que faire se pouvait les conversions de int vers float , un besoin des utilisateurs depuis toujours... (Fotran a été fait pour répondre aux besoins des utilsateurs, qui ne sont pas forcèment des PRO: quand ils demandent de structurer les programmes, -donc de copie partiellement Algol, Pascal, voire C- ils attendent de trouver des developeurs pour faire un fortran77 fiable; quand ils demandent de stucturer partiellement les données - ala cobol, Pascal et C- on a Fortran 90...

Quant à la prise en compte des standard int en fortran: la tendance naturelle, quand on fait du calcul, est d'exploiter le maximum de bits disponibles, sans se casser la tête (sur un PC ou au delà, ce n'est pas absurde).
Même maintenant, R -très populaire en stats- ne connaît "que" les entiers et les réels au maximum de la précision (Splus, que R clone, est commercial et permet de gérer plus finement les types d'entiers: peu de gens l'achètent pour ça)
En plus, certains monstres prehistoriques Control Data 6600 — Wikipédia avaient des mots de 60 bits.... l'utilisation de stdint aurait été une horreur (perte de 56/120 de la mémoire qui était chère- . (en passant, les chars pouvaient être sur 6 bits: 26 lettres majuscules, 10 chiffres et 28 autres: d'où le fait que le Fortran standard s'écrit en majuscules -et que personne ne respecte ce standard-).

A noter aussi que Fortran permet, maintenant, de distinguer les arguments d'entrée, de sortie et modifiés de fonctions/sous programmes.... (Fortran 66 était très folklorique de ce point de vue )

Quand à la différence entre float() et (float): même après optimisation, ils sont differents sous C++ pour PC (g++ 4.8.9) -le resultat devant être le même- : l'usage d'une fonction fait que, avec la même optimisation que pour arduino "size", float() est trop long ...4 octets (sur 600...)

J’ai pas bien compris votre dernier paragraphe

Bon,
j'ai repris la même fonction que dans le post original et fait une toute petite modif pour les tests sur PC (avce g++ 4.8.2)

float CalculLReserv(int ValRes, int Min, int Max, int L) {
#ifdef FLOATFUNCTION
  return (L + (float(ValRes - Min) / (Max - Min)));
#else

  return (L + ((float)ValRes - Min) / ((Max - Min)));
#endif
}

Là, il est très facile, quoique lourd de faire quelques tests
Sans niveau d'optimisation (-O1 je pense)

 g++  -DFLOATFUNCTION -c reserv.cpp -o DFLOATFUNCTIONreserv.o  && g++  -c reserv.cpp -o reserv.o  && ls  -l DFLOATFUNCTIONreserv.o  reserv.o
-rw-r--r-- 1 xxx yyy 675  1 août  13:29 DFLOATFUNCTIONreserv.o
-rw-r--r-- 1 xxx yyy 667  1 août  13:29 reserv.o

Avec niveau d'optimisation 3 (-O3) : les deux .o ont tous deux pour longueur 667 (mais différent selon "diff"
se servir de la taille des fichiers generes est insuffisant pour conclure à l'égalité)
Avec niveau d'iptimisation s (-Os) : le fichier utilisant float() a 4 octets de plus que cemui utilisant le cast -663 vs 659)