Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Promotion entiere

12 réponses
Avatar
candide
Bonjour,

D'abord, un point de vocabulaire : en français, parle-t-on de "promotion
entière" ou de "promotion intégrale" ?

D'autre part, le passage de la norme qui parle de cette question est, à
mon sens, trop fortement obscurci par une note de bas de page. Plus
précisément, il est dit :

------------------------8<-----------------------------
6.3.1.1p2
(...)If an int can represent all values of the original type, the value
is converted to an int; otherwise, it is converted to an unsigned int.
These are called the integer promotions.(48)

La note de bas de page :

(48) -- The integer promotions are applied only: as part of the usual
arithmetic conversions, to certain argument expressions, to the operands
of the unary +, -, and ~ operators, and to both operands of the
shift operators, as specified by their respective subclauses.
------------------------>8-----------------------------

Si j'ai bien lu la norme, les conversions arithmétiques usuelles
s'appliquent uniquement à des opérateurs binaires et il faut regarder
dans la norme au cas par cas lesquels sont concernés.
Concernant les "argument expressions", bon je suppose les expressions
qui sont arguments de fonctions, lesquelles sont concernées ? K&R parle
de ceci :

------------------------8<-----------------------------
In the absence of a function prototype, char and short become int,
------------------------>8-----------------------------

c'est à ça qu'il est fait allusion ?

Je crois que beaucoup de livres de C n'ont pas tenu compte de cette note
de pas de page. Par exemple, dans le livre de Delannoy, je lis (page
78, §3.1) :
------------------------8<-----------------------------
"Aucun opérateur n'accepte d'opérandes de type char ou short."
------------------------>8-----------------------------

C'est vrai ou pas ?

De même dans le livre de Braquelaire, je lis :

------------------------8<-----------------------------
"Lors de l'évaluation d'une expression, un opérande d'un type intégral
caractère, entier court, champ de bits ou énumération est converti dans
le type int ou dans le type unsigned int si le type int n'est pas assez
grand pour représenter toutes les valeurs de son type. Cette conversion
s'appelle promotion intégrale. "
------------------------>8-----------------------------
mais aucune allusion à la note de bas de page.

Plus généralement, avez-vous des exemples simples où la promotion
entière ne s'applique pas ?

Merci

10 réponses

1 2
Avatar
espie
In article <484a9511$0$13044$,
candide wrote:
Je crois que beaucoup de livres de C n'ont pas tenu compte de cette note
de pas de page. Par exemple, dans le livre de Delannoy, je lis (page
78, §3.1) :
------------------------8<-----------------------------
"Aucun opérateur n'accepte d'opérandes de type char ou short."
------------------------>8-----------------------------

C'est vrai ou pas ?


Ca ne me surprend pas de Delannoy, que je n'aime pas trop.

De même dans le livre de Braquelaire, je lis :

------------------------8<-----------------------------
"Lors de l'évaluation d'une expression, un opérande d'un type intégral
caractère, entier court, champ de bits ou énumération est converti dans
le type int ou dans le type unsigned int si le type int n'est pas assez
grand pour représenter toutes les valeurs de son type. Cette conversion
s'appelle promotion intégrale. "
------------------------>8-----------------------------


Ces deux bouquins font reference au C pre-norme, dit aussi
C Kernighan & Richie, dans lequel les regles de promotion sont beaucoup
plus simples: les calculs se font systematiquement sur des int ou des
double, il n'y a pas d'arithmetique sur des short ou des float.

La norme explicite la possibilite d'avoir le calcul sur les float, et
reste tres evasive sur les entiers. Elle explique qu'on peut avoir
conversion vers des int, ou assimiles, mais ca n'a vraiment pas l'air
d'etre obligatoire.

Ca ressemble fort a la possibilite de permettre a un compilateur travaillant
dans un environnement un peu specifique de faire ses calculs directement
sur des short, par exemple...

Typiquement, un gcc sur i386 va faire ses calculs sur des short si on lui
en laisse la possibilite.

Par exemple:

struct point {
short x, y;
};

short f(struct point *p)
{
return p->x + p->y;
}

se compile en:
f:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movw 2(%edx), %ax
addw (%edx), %ax
cwtl
leave
ret


Le plus naif
short f(short a, short b)
donne un calcul sur des mots de 32 bits, lie a l'ABI standard de mon
systeme, qui meme en presence de prototypes, passe les short par mots
de 32 bits sur la pile...

Avatar
Vincent Lefevre
Dans l'article <g2e6b0$25tr$,
Marc Espie écrit:

La norme explicite la possibilite d'avoir le calcul sur les float, et


C'est d'ailleurs nécessaire pour le support IEEE 754, car un calcul
en double précision puis un arrondi en simple précision (float) ne
donne pas toujours le même résultat qu'un calcul direct en simple
précision (effet du double arrondi).

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

Avatar
Antoine Leca
En news:484a9511$0$13044$, candide va escriure:
D'abord, un point de vocabulaire : en français, parle-t-on de
"promotion entière" ou de "promotion intégrale" ?


En français, « promotion intégrale » va faire penser à la promotion de la
totalité du sujet (cf. intégralité), donc perdre la relation avec le mot «
entier ».
Maintenant, dans le jargon des spécialiste du langage C, j'imagine que tu
vas trouver les deux.

D'autre part, le passage de la norme qui parle de cette question est,
à mon sens, trop fortement obscurci par une note de bas de page.


Une note de bas de page a un caractère moins contraignant que le texte de la
norme (c'est comme comparer une explication donnée par un code Dalloz avec
le texte d'une loi) ; donc si ce n'est pas clair _avec_ la note, tu fais
abstraction de la note.

Par ailleurs, et même si c'est moins facile à consulter que le snotes de bas
de page, il est bon de lire aussi le paragraphe correspondant du Rationale.
En l'occurence tu vas avoir plus que ce que tu ne cherches (par exemple, une
discussion sur la différence entre préservation de la valeur ou préservation
de la qualité d'être signé).


------------------------8<-----------------------------
6.3.1.1p2
(...)If an int can represent all values of the original type, the
value is converted to an int; otherwise, it is converted to an
unsigned int. These are called the integer promotions.(48)

La note de bas de page :

(48) -- The integer promotions are applied only: as part of the usual
arithmetic conversions, to certain argument expressions, to the
operands of the unary +, -, and ~ operators, and to both operands of
the shift operators, as specified by their respective subclauses.
------------------------>8-----------------------------

Si j'ai bien lu la norme, les conversions arithmétiques usuelles
s'appliquent uniquement à des opérateurs binaires


Raison pour laquelle la note cite explicitement les autres cas (en dehors
des conversions usuelles) où les promotions entières vont être appliquées.

et il faut regarder dans la norme au cas par cas lesquels sont
concernés.


Si tu as un meilleur texte à proposer (en anglais) pour la note, tu peux
lancer une discussion sur comp.std.c ; Larry Jones (le type qui écrit le
texte de la norme) suit ce groupe, et on est dans le bon timing pour que ce
genre de corrections (dites « éditoriales ») soient prises en compte dans la
prochaine moûture.
Un coseil : suit un peu le groupe avant de rentrer dedans de plein pied : il
faut un peu d'expérience (et la lecture assidue des archives) pour savoir si
tel ou tel intervenant est un trolleur fou ou quelqu'un de respecté ; et il
arrive assez souvent que la première réponse y compris émanent d'un gars
sérieux soit faite sans trop réfléchir, surtout si la question ou le style
ou l'auteur n'est pas habituelle (et nous autres Français avons semble-t-il
une manière assez différente d'aborder le texte normatif par rapport aux
Américains).


Concernant les "argument expressions", bon je suppose les expressions
qui sont arguments de fonctions, lesquelles sont concernées ?


Il y a deux pages de texte dans 6.5.2.2 qui sont quasiment exclusivement
dédiées à ce sujet, donc je ne vais pas faire un long exposé (avec le risque
élevé de me planter :-B).


Je crois que beaucoup de livres de C n'ont pas tenu compte de cette
note de pas de page.


En fait, beaucoup de livres de C cherchent à rendre accessible un sujet ; de
ce fait les auteurs peuvent choisir des raccourcis si cela rend leur propos
plus clair, peut-être au détriment de certains cas particuliers. La norme de
son côté n'a pas cet objectif, elle cherche à définir un contrat (entre
l'implémenteur du compilateur et le programmeur) en laissant le moins de
marge possible aux ambiguïtés.


Par exemple, dans le livre de Delannoy, je lis :
"Aucun opérateur n'accepte d'opérandes de type char ou short."


Quand tu lis « Aucun » ou n'importe quel mot définitif de cet acabit sur un
sujet qui par ailleurs paraît complexe, il y a deux possibilités avec des
probabilités très inégales :
- celui qui présente le sujet de manière très complexe est un nul ou
cherche délibérement à emberlificoter (cas typique du texte d'un brevet) ;
- le jugement définitif est à l'emporte-pièce.


C'est vrai ou pas ?


Bin non. Par exemple, dans
char i; int j = sizeof i;
l'opérande (ici i) de l'opérateur sizeof n'est pas promu, et reste de type
char. Et heureusement, parce que sinon on voit mal l'intérêt de l'opérateur
sizeof dans le cas général (dans ce cas précis par contre, cela peut avoir
une utilité que n'a pas le code que j'ai écrit ;-) ).


Plus généralement, avez-vous des exemples simples où la promotion
entière ne s'applique pas ?


Appel de fonction (avec prototypes de types char ou short), conversions, et
affectation à un char ou un short (évidemment !)

Il est aussi des cas où la promotion ne s'applique pas mais cela n'a aucun
effet (visible) : par exemple, quand on ajoute un entier à un pointeur, il
n'y a pas dans la norme de promotion sur l'entier, mais il est de toutes
manières impossible de voir l'effet d'une éventuelle promotion ; idem pour
l'opérateur ! appliqué à un char ou un short.
Après cela, que le compilateur fasse (ou pas) une extension de signe pour
utiliser tel ou tel mode d'adressage ou instruction du processeur, ici tout
est libre, car le seul le résultat compte !


Antoine

Avatar
candide

Si tu as un meilleur texte à proposer (en anglais) pour la note, tu peux
lancer une discussion sur comp.std.c ;



;)



Concernant les "argument expressions", bon je suppose les expressions
qui sont arguments de fonctions, lesquelles sont concernées ?


Il y a deux pages de texte dans 6.5.2.2 qui sont quasiment exclusivement
dédiées à ce sujet, donc je ne vais pas faire un long exposé (avec le
risque

élevé de me planter :-B).


Je crois que beaucoup de livres de C n'ont pas tenu compte de cette
note de pas de page.


En fait, beaucoup de livres de C cherchent à rendre accessible un
sujet ;


Justement, une des choses les plus difficiles du C je trouve est (je
parle en tant que post-débutant) de comprendre tout ce qui tient aux
conversions et aux cast.



Bin non. Par exemple, dans
char i; int j = sizeof i;
l'opérande (ici i) de l'opérateur sizeof n'est pas promu, et reste de
type

char.


Tiens oui, en effet. J'espère que la norme explique ça quelque part (je
dis ça parce que je ne vois pas ce point dans le paragraphe 6.5.3.4 The
sizeof operator).


Avatar
Wykaaa

Si tu as un meilleur texte à proposer (en anglais) pour la note, tu peux
lancer une discussion sur comp.std.c ;



;)



Concernant les "argument expressions", bon je suppose les expressions
qui sont arguments de fonctions, lesquelles sont concernées ?


Il y a deux pages de texte dans 6.5.2.2 qui sont quasiment exclusivement
dédiées à ce sujet, donc je ne vais pas faire un long exposé (avec le
risque

élevé de me planter :-B).


Je crois que beaucoup de livres de C n'ont pas tenu compte de cette
note de pas de page.


En fait, beaucoup de livres de C cherchent à rendre accessible un
sujet ;


Justement, une des choses les plus difficiles du C je trouve est (je
parle en tant que post-débutant) de comprendre tout ce qui tient aux
conversions et aux cast.



Bin non. Par exemple, dans
char i; int j = sizeof i;
l'opérande (ici i) de l'opérateur sizeof n'est pas promu, et reste de
type

char.


Tiens oui, en effet. J'espère que la norme explique ça quelque part (je
dis ça parce que je ne vois pas ce point dans le paragraphe 6.5.3.4 The
sizeof operator).

Et bien heureusement que i reste de type char puisqu'il estde type... char.


Quand on parle de promotion (dans tous les langages avec des conversions
implicites et pas seulement en C) d'une variable dans un autre type, il
ne s'agit pas de la conversion de la variable elle-même mais de la
création, par le compilateur, d'une variable temporaire qui contiendra
le résultat de la promotion (comme pour le passage d'arguments par valeur).
Attention aux confusions !!



Avatar
candide

Et bien heureusement que i reste de type char puisqu'il estde type... char.



Et qui a dit le contraire ? La question était de savoir où la norme
explique qu'il n'y a pas lieu de convertir ainsi par exemple qu'elle
l'explique pour un tableau opérande de l'opérateur sizeof.


Quand on parle de promotion (dans tous les langages avec des conversions
implicites et pas seulement en C) d'une variable dans un autre type, il
ne s'agit pas de la conversion de la variable elle-même mais de la
création, par le compilateur, d'une variable temporaire qui contiendra
le résultat de la promotion (comme pour le passage d'arguments par valeur).
Attention aux confusions !!


Quelles confusions ? celles que tu as eu faites peut-être ? D'ailleurs,
tu spécules sur ce que dit la norme à ce sujet, elle ne parle pas de la
création de "variable temporaire", elle dit juste qu'il y a conversion
de la _valeur_ d'un opérande d'un type vers un autre. Comment ? ça on en
sait rien. Et même pour le passage par valeur, la norme ne parle pas de
"variable temporaire" ou de pile, elle dit seulement ce qui se passe
mais pas comment, puisqu'on lit seulement, dans une note de bas de page :

--------------------------------- 8< --------------------------
A function may change the values of its parameters, but these
changes cannot affect the values of the arguments.
--------------------------------- >8 --------------------------

Pour en revenir à ma question, à laquelle tu n'as pas répondu, la norme
en fait dit qu'il n'y a pas de conversion de l'opérande d'un sizeof
quand elle dit :

--------------------------------- 8< --------------------------
The size is determined from the type of the operand, which is not itself
evaluated.
--------------------------------- >8 --------------------------

S'il n'y a pas évaluation, il n'y a pas conversion.


Avatar
Wykaaa
candide a écrit :
Wykaaa a écrit :



Et bien heureusement que i reste de type char puisqu'il estde type...
char.



Et qui a dit le contraire ? La question était de savoir où la norme
explique qu'il n'y a pas lieu de convertir ainsi par exemple qu'elle
l'explique pour un tableau opérande de l'opérateur sizeof.


Quand on parle de promotion (dans tous les langages avec des
conversions implicites et pas seulement en C) d'une variable dans un
autre type, il ne s'agit pas de la conversion de la variable elle-même
mais de la création, par le compilateur, d'une variable temporaire qui
contiendra le résultat de la promotion (comme pour le passage
d'arguments par valeur).
Attention aux confusions !!



Quelles confusions ? celles que tu as eu faites peut-être ? D'ailleurs,
tu spécules sur ce que dit la norme à ce sujet, elle ne parle pas de la
création de "variable temporaire", elle dit juste qu'il y a conversion
de la _valeur_ d'un opérande d'un type vers un autre. Comment ? ça on en
sait rien. Et même pour le passage par valeur, la norme ne parle pas de
"variable temporaire" ou de pile, elle dit seulement ce qui se passe
mais pas comment, puisqu'on lit seulement, dans une note de bas de page :

--------------------------------- 8< --------------------------
A function may change the values of its parameters, but these
changes cannot affect the values of the arguments.
--------------------------------- >8 --------------------------



Il suffit de regarder l'assembleur généré par les compilateurs pour
s'apercevoir que tous créent des variables temporaires pour ce faire.
J'ai fait des compilateurs. Je sais de quoi je parle.
Explique-moi comment faire un passage par valeur sans créer une variable
temporaire sur la pile :-).
C'est ce temporaire qui est affecté dans la fonction si le paramètre est
passé par valeur et qu'il est affecté dans la fonction. Point barre.

Pour en revenir à ma question, à laquelle tu n'as pas répondu, la norme
en fait dit qu'il n'y a pas de conversion de l'opérande d'un sizeof
quand elle dit :

--------------------------------- 8< --------------------------
The size is determined from the type of the operand, which is not itself
evaluated.
--------------------------------- >8 --------------------------

S'il n'y a pas évaluation, il n'y a pas conversion.



Il n'y a pas lieu de faire une conversion pour évaluer la taille du
"conteneur" d'une variable puisque ceci est "câblé" dans un compilateur
(en fonction de la machine cible où que, comme en Java, chaque type a
une longueur définie par le langage.
Avatar
espie
In article <485becb1$0$913$,
Wykaaa wrote:
Il suffit de regarder l'assembleur généré par les compilateurs pour
s'apercevoir que tous créent des variables temporaires pour ce faire.
J'ai fait des compilateurs. Je sais de quoi je parle.
Explique-moi comment faire un passage par valeur sans créer une variable
temporaire sur la pile :-).



Trop peremptoire et faux. Sur pas mal de processeurs, une grosse
proportion des passages de parametres par valeur se font par registres.

Sur des compilateurs un peu plus futes que ceux que tu as ecrit, il peut
y avoir une analyse de flux suffisante pour se passer de toute copie
si elle n'est pas necessaire (par exemple, si un parametre est passe
a une fonction feuille, et que cette fonction ne modifie pas ce parametre,
le compilateur peut tres bien passer la variable elle-meme si elle est
deja dans le bon registre, voire s'arranger pour allouer la variable
dans le bon registre directement).

La norme est justement ecrite par des gens qui comprennent le fonctionnement
de ce genre de compilateurs, et qui laissent suffisamment de marge a
l'implementeur pour pouvoir mettre en place ce type d'optimisation.


C'est ce temporaire qui est affecté dans la fonction si le paramètre est
passé par valeur et qu'il est affecté dans la fonction. Point barre.




Ca fait beaucoup d'hypotheses non necessairement verifiees dans certains
cas pratiques, tout ca.
Avatar
Wykaaa
Marc Espie a écrit :
In article <485becb1$0$913$,
Wykaaa wrote:
Il suffit de regarder l'assembleur généré par les compilateurs pour
s'apercevoir que tous créent des variables temporaires pour ce faire.
J'ai fait des compilateurs. Je sais de quoi je parle.
Explique-moi comment faire un passage par valeur sans créer une variable
temporaire sur la pile :-).



Trop peremptoire et faux. Sur pas mal de processeurs, une grosse
proportion des passages de parametres par valeur se font par registres.

Sur des compilateurs un peu plus futes que ceux que tu as ecrit, il peut
y avoir une analyse de flux suffisante pour se passer de toute copie
si elle n'est pas necessaire (par exemple, si un parametre est passe
a une fonction feuille, et que cette fonction ne modifie pas ce parametre,
le compilateur peut tres bien passer la variable elle-meme si elle est
deja dans le bon registre, voire s'arranger pour allouer la variable
dans le bon registre directement).



Oui tu as raison, il y a des cas où le paramètre est en registre mais ce
n'est pas toujours possible, ça dépend de ce qu'en fait la fonction et
ça dépend aussi si la fonction est (potentiellement) récursive. Et puis
ça dépend aussi du nombre de registres de la machine. A l'époque, il y
avait très peu de registres et on les réservait aux calculs
intermédiaires qui étaient susceptibles d'être réutilisés par la suite.

Ce que j'ai décrit est la façon "orthodoxe" de faire, celle qui
n'entraîne aucune surprise, ni pour le développeur, ni pour l'éventuel
"patcheur" futur.

La norme est justement ecrite par des gens qui comprennent le fonctionnement
de ce genre de compilateurs, et qui laissent suffisamment de marge a
l'implementeur pour pouvoir mettre en place ce type d'optimisation.



Je n'ai jamais dit que la norme devait indiquer comment implémenter le
passage par valeur. Effectivement, la norme doit laisser aux
implémentations la façon de traiter les choses.


C'est ce temporaire qui est affecté dans la fonction si le paramètre est
passé par valeur et qu'il est affecté dans la fonction. Point barre.




Ca fait beaucoup d'hypotheses non necessairement verifiees dans certains
cas pratiques, tout ca.



Il y a pleins de contextes différents et donc d'hypothèses différentes
pour les passages de paramètres aux fonctions dans les langages de
programmation.
Par exemple, une fonction déclarée statique et qui ne fait pas l'objet
de récursion peut très bien être appelée par un branchement (dans
l'assembleur généré. Il n'y aura pas de nouvel étage de pile créé) avec
mémorisation de l'adresse retour. Dans ce cas, le compilateur peut
effectivement très bien conserver les paramètres en registres (si la
fonction ne les modifie pas). Dans le cas contraire, le plus simple
reste de stocker les paramètres dans des zones temporaires.
Avatar
Antoine Leca
En news:485becb1$0$913$, Wykaaa va escriure:
Il suffit de regarder l'assembleur généré par les compilateurs pour
s'apercevoir que tous créent des variables temporaires pour ce faire.
J'ai fait des compilateurs. Je sais de quoi je parle.
Explique-moi comment faire un passage par valeur sans créer une
variable temporaire sur la pile :-).



Soit en utilisant des registres, soit en utilisant une pile alternée (genre
les fenêtres de registres de certains RISC des années 90 ; je n'ai pas les
noms en tête et suis trop flemmard pour chercher), soit en simulant le
passage par valeur au moyen d'un passage par référence suivi d'une copie
locale (très utilisé pour les structures).
Ce qui fait trois métodes différentes en 10 secondes et sans chercher.

Et avec une minute de plus (c-à-d en relisant le fil), il y a aussi le fait
de ne rien créer du tout, soit parce que le compilo « voit » que la
conversion est inutile et que l'on peut économiser (rien n'interdit de faire
une opération sur 8 bits si le résultat tient dans cette taille, et
d'ailleurs beaucoup de compilos ne vont pas s'en priver), soit plus fort
parce que l'optimiseur fait une analyse globale et élimine purement et
simplement l'opération en question (et sans analyse globale, n'importe quel
compilateur C réduit les constantes, et zou, y'a pù d'promotion au final ;
OK, il faut un optimisateur global pour faire cela au travers d'un appel de
fonction, mais le principe reste le même).


Antoine
1 2