OVH Cloud OVH Cloud

A quoi servent > pour les entiers?

31 réponses
Avatar
Michaël Delva
Bonjour,

j'ai souvent vu ces opérateurs utilisés pour des entiers, avec le terme
décalage associé, mais je ne sais pas à quoi cela sert réellement...

Une âme charitable pourrait m'éclairer?

Merci d'avance

10 réponses

1 2 3 4
Avatar
Pierre Maurette
"Michaël Delva" typa:
[...]
Et pourquoi le décalage alors qu'on peut tout simplement multiplier?
Il ne faut pas décaler quand on veut multiplier.

Il faut coder ce qu'on veut faire et laisser travailler le
compilateur, qui DOIT savoir que faire (sur la release optimisée) en
cas de multiplication par une valeur constante à la compilation et
quand l'instruction machine de multiplication (si elle existe) est
couteûse. En codant naturellement une multiplication et en choisissant
de bons compilateurs, on maximise ses chances d'avoir le meilleur
résultat sur plusieurs implémentations.
Gag: pour coder une multiplication par une constante immédiate en
assembleur, plutôt que de regarder telle ou telle table
d'optimisation, je "pirate" parfois la sortie d'un compilateur ;-).
Il peut éventuellement y avoir des cas particuliers, si par exemple
l'analyse amène à des expressions de la forme:
resultat = valeur() * X; // où X est toujours de la forme 2^N

Et ça ne s'utilise qu'avec des entiers?
Oui (voir norme plus bas).

Mais en castant dans l'entier non signé de la bonne taille (s'il
existe), on peut décaler la représentation mémoire de n'importe quoi.
Les utilisations ne sont certainement pas légion. Eventuellement,
combiné à des masquages, des conversions de format de flottants...

Bref, dans quels cas utilise-t-on le décalage?
AMHA, il n'y a pas de cas où << et >> sont irremplaçables. Les * et /,

assortis du cast en signed ou unsigned qui va bien, permettent de tout
faire. C'est donc comme souvent la sémantique qui déterminera le choix
éventuel des instructions de décalage. Si je veux récupérer l'exposant
d'un float par décalage de 23 positions, j'ai le choix entre:
??? = ((unsigned int)x) >> 23;
[commentaire inutile]
et
??? = ((unsigned int)x) * 8388608; // 8388608 = 2^23, décalage à
// droite de 23 positions
Choisis ton camp, camarade ...


(Un exemple fatalement non portable) Imaginons que nous récupérions
une valeur d'acquisition sur 12 bits signés en complément à 2. Peu
importe si d'où cette valeur provient, elle est fournie dans un entier
plus grand:
short mesure = Acq();
(short est sur 16 bits, complément à 2).
Manque de bol, les 4 bits forts de mesure sont indéterminés (ou tous à
1 ou tous à 0), bref ils ne recopient pas le bit de signe de la valeur
sur 12 bits (bit 11 de mesure). Il faut donc faire l'opération à la
main :
mesure = (short)(mesure << 4) / 16;
(désolé, cast à la C)

mesure = (short)(mesure << 4) >> 4;
fonctionnera "souvent" mais la précédente est "plus portable".

mesure = (short)(mesure * 16) / 16;
Fonctionne, mais c'est moins "parlant". Enfin, pour moi ...

Personnellement, les cast et autre promotions de type m'effraient un
peu. Non portable pour non portable, une petite macro de 2 lignes
d'_asm et je sais ce qui se passe.
#define EXPAND12to16(nomvar)
_asm {shl word ptr (nomvar), 4;
sar word ptr (nomvar), 4;
}
Ou alors Java, supérieur à C/C++ sur les décalages :-(

La norme dit plein de trucs. En fait, la représentation du signe
n'étant pas normalisée, il est difficile d'être plus précis sur les
décalages des signed.
Pour un entier non signé, on a un décalage de bits avec remplissage de
0.
Pour une entier signé, le décalage >> devrait généralement propager le
signe (entrer des 0 si positif, des 1 si négatif). Le décalage <<,
dans les implémentation (complément à 2) que je connais, ignore le
signe. Il décale la représentation physique comme pour un non signé.
Que pourrait-il faire d'autre ?

Extrait de C99 (C++ à peu différent):

6.5.7 Bitwise shift operators
Syntax
1 shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression

Constraints
2 Each of the operands shall have integer type.

Semantics
3 The integer promotions are performed on each of the operands. The
type of the result is that of the promoted left operand. If the value
of the right operand is negative or is greater than or equal to the
width of the promoted left operand, the behavior is undefined.

4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated
bits are filled with zeros. If E1 has an unsigned type, the value of
the result is E1 * 2^E2, reduced modulo one more than the maximum
value representable in the result type. If E1 has a signed type and
nonnegative value, and E1 * 2^E2 is representable in the result type,
then that is the resulting value; otherwise, the behavior is
undefined.

5 The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1
has an unsigned type or if E1 has a signed type and a nonnegative
value, the value of the result is the integral part of the quotient of
E1 / 2^E2. If E1 has a signed type and a negative value, the resulting
value is implementation-defined.
--
Pierre

Avatar
kanze
"Michaël Delva" wrote in message
news:...
Raffaello wrote in
news:cbphjk$ep9$:

Ca sert a decaler les bits.
par exemple :
2 en binaire : 00000010
si tu fais 2 << 1
tu as : 00000100, ce qui fait 4.
ou 2 >> 1, ca te donne 00000001, ce qui fai 1.
en fait tu decale tous les bits vers la gauche ou vers la droite du
nombre donner.
Un dernier exemple :
21 = 10101
21 << 1 = 101010 (42)
21 << 2 = 1010100 (84)
...
au final ca revien a une mutiplication par 2 pour chaque decalage.


Et pourquoi le décalage alors qu'on peut tout simplement multiplier?


C'est une question de lisabilité. Si je considère les entiers comme des
valeurs entières, il faut multiplier. Mais il y a des fois où on pense
réelement aux bits. Donc, par exemple, si tu transmets des entiers
binaires de quatre octets sur le reseau, il faut émettre l'octet de
poids fort d'abord, puis le deuxième, etc. C-à-d :

(valeur >> 24) & 0xff
(valeur >> 16) & 0xff
(valeur >> 8) & 0xff
(valeur ) & 0xff

Tu pourrais bien écrire la même chose avec la division -- il y aurait un
problème avec les valeurs négatives, mais si tu affectes d'abord à un
type non-signé, et tu l'utilises, c'est bon. (Et c'est une bonne
stratégie aussi avec le décalage -- ça garantit le bon fonctionnement
même sur des machines qui ne sont pas complément à deux.)

De la même façon, si tu veux émettre un champs de longueur dans BER --
c'est un entier à longueur variable :

do {
unsigned char octet = valeur & 0x7f ;
valeur >>= 7 ;
if ( valeur != 0 ) {
octet |= 0x80 ;
}
output( octet ) ;
} while ( valeur != 0 ) ;

On veut traiter 7 bits à la fois. On pourrait bien écrire valeur /= 128,
mais je trouve le fait d'écrire >>= 7 beaucoup plus parlant -- on écarte
les 7 bits de poids faibles.

Et ça ne s'utilise qu'avec des entiers?


Tout à fait. Parce qu'il n'y a que des entiers avec un format assez
simple pour qu'il a un sens.

Dans la pratique, il sert surtout sur des entiers non signés ; au moins
dans les boîtes où j'ai travaillé, le fait de déclarer un type
«@unsigned » était pratiquement une déclaration qu'on allait en tripoter
les bits (à moins qu'on le fasse parce qu'il fallait un résultat
modulo).

Bref, dans quels cas utilise-t-on le décalage?


Quand on veut décaler un nombre de bits. C'est très utiliser dans la
génération des formats binaires pour le reseau. C'est occasionnellement
utilisé dans les bibliothèques flottant, pour extraire (d'une façon
qui dépend fortement de l'implémentation) l'exposant d'un nombre
flottant -- sur Intel IA-32, par exemple :

int
getExponent( double d )
{
unsigned short rawExp
= (*reinterpret_cast< unsigned short* >( &d + 3 ) >> 4) & 0x7ff ;
return static_cast< int >( rawExp ) - 1023 ;
}

En général, quand il y a >> ou <<, il y aurait aussi &, | ou ^ et des
constantes hexadécimales ou octales. On manipule des bits, et non des
valeurs sur l'entier complet.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Fabien LE LEZ wrote in message
news:...
On Mon, 28 Jun 2004 19:26:28 +0200, Vincent Richard
:
peine de se casser la tête...


On utilise << et >> quand on veut vraiment faire du décalage de bits,
i.e. quand on s'intéresse à la structure d'un entier.
Par exemple, si un DWORD (= entier non signé de 32 bits) contient en
fait deux WORD (= entier non signé de 16 bits), le WORD de poids fort
étant "machin" et le WORD de poids faible "truc", on écrira :

WORD machin= mon_DWORD >> 16;
Voire :
WORD machin= mon_DWORD >> (sizeof WORD * CHAR_BIT);

Ce genre de décalage de bits est utilisé également dans certains
algorithmes, comme CRC32.


En effet. Je crois que plus de 90% des utilisations du décalage que j'ai
fait a eu à faire avec des protocols de transmissions. Avec les largeurs
de bandes disponibles aujourd'hui, la tendence dans les protocols
nouveaux, c'est de voir large, et de se servir d'un octet même quand
l'élément ne peut prendre que trois ou quatre valeurs. Mais ce n'était
pas toujours le cas, que si on jette un coup d'oeil à des protocols plus
anciens (à commencer par RFC 791 et RFC 793 -- IP et TCP), on trouve
plein de champs à quatre bits ou d'autres. (Voir l'octet « type of
service » dans RFC 791, par exemple.)

En résumé, si tu veux faire une multiplication ou une division, tu
utilises * ou /, et tu laisses le compilo se depêtrer. Si un
algorithme parle de décalage de bits, tu utilises << et >>.


Tout à fait.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Alexandre BACQUART wrote in message
news:<40e0516c$0$29365$...
Michaël Delva wrote:

j'ai souvent vu ces opérateurs utilisés pour des entiers, avec le
terme décalage associé, mais je ne sais pas à quoi cela sert
réellement...

Une âme charitable pourrait m'éclairer?


C'est un héritage du C.


Plusieurs l'ont dit. C'est vrai qu'en C++, tous les opérateurs sur les
entiers sont des héritages du C. Mais est-ce que tu dirais que
l'opérateur + est un héritage de C ?

Autant que je sache, l'opérateur est présent dans tous les langages à
prétention de programmation système. C'est une opération fondamentale
pour le formattage des messages binaire, par exemple. Dans le temps,
c'était assez courant au niveau de l'OS d'encoder plusieurs valeurs dans
un mot. La date, sous MS-DOS, par exemple, était un mot de 16 bits où
les 5 bits de poids faibles représentait le jour, le 4 suivant le mois,
et les 7 de poids forts l'année. Pour en extraire les parties, on
décalait et masquait.

Même aujourd'hui, en partie pour des raisons historiques, ces genres de
formats sont légions.

[...]
Décaler les bits d'un entier a la propriété de multiplier/diviser par
2 puissance N la valeur qu'il représente. Si tu décales à gauche, tu
multiplies, si tu décales à droite tu divises.


C'est vrai pour les valeurs non-signées. Pour les valeurs signées, il
faut faire gaffe. La norme donne une certaine liberté à
l'implémentation.

[...]
Ca ne marche qu'avec des entiers et si l'opérateur est défini pour
autre chose, ça fait quelque-chose de très différent et rarement
comparable.


En C++, les opérateurs << et >> sont surtout les opérateurs
d'entrées/sorties. (Ce qui n'est pas un héritage de C.)

Attention aussi aux débordements. C'est aussi une manière de ne pas
faire confiance au compilateur pour optimiser des
multiplications/divisions par des puissances de 2 en invoquant
soi-même l'opération de décalage (souvent un mnémonique dédiée du
processeur).


Sauf qu'il y a des processeurs où la multiplication est plus rapide que
le décalage, et où ce n'est pas le cas, le compilateur en général sait
mieux faire que toi.

On ne s'en sert jamais pour l'optimisation. On s'en sert quand ce qu'on
veut faire, c'est de positionner des bits, et non de multiplier ou de
diviser. En fait, il y a beaucoup de programmeurs qui n'en aurait jamais
besoin. (En revanche, j'ai fait beaucoup de programmation reseau dans ma
carrière, et je m'en suis servi peut-être autant que de la
multiplication, et certainement plus que de la division.)

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Jean-Marc Bourguet
writes:

Autant que je sache, l'opérateur est présent dans tous les langages
à prétention de programmation système.


Si j'ai bonne memoire, Ada n'a pas de tels operateurs. Et s'il y a un
marche ou Ada a une presence autre qu'anectodique, ce sont les
systemes embarques.

A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Michaël Delva
En général, quand il y a >> ou <<, il y aurait aussi &, | ou ^ et des
constantes hexadécimales ou octales. On manipule des bits, et non des
valeurs sur l'entier complet.


A ce propos, vous auriez de la doc sur ces symboles?

Avatar
kanze
"Michaël Delva" wrote in message
news:...
En général, quand il y a >> ou <<, il y aurait aussi &, | ou ^ et
des constantes hexadécimales ou octales. On manipule des bits, et
non des valeurs sur l'entier complet.


A ce propos, vous auriez de la doc sur ces symboles?


Que ce qu'il y a dans la norme, ou dans K&R première édition. Ce sont
les et, ou et xor bit-à-bit. (Je viens du hardware. Je n'ai donc jamais
eu besoin de plus d'explication que ça.)

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Jean-Marc Bourguet wrote in message
news:...
writes:

Autant que je sache, l'opérateur est présent dans tous les langages
à prétention de programmation système.


Si j'ai bonne memoire, Ada n'a pas de tels operateurs. Et s'il y a un
marche ou Ada a une presence autre qu'anectodique, ce sont les
systemes embarques.


Dans la plupart des langages de la famille Pascal, ils sont présentent
sous la forme des fonctions prédéfinies. J'aurais dû dire que
l'opération (et non l'opérateur) est présent.

En revanche, en ce qui concerne Ada... §4.5.1/2 :

The following logical operators are predefined for every boolean
type T, for every modular type T, and for every one-dimentional
array type T whose component type is a boolean type :

function "and"(Left, Right : T) return T
function "or" (Left, Right : T) return T
function "xor"(Left, Right : T) return T

For boolean types, the predefined logical operators and, or and xor
perform the conventional operations of conjunction, inclusive
disjunction and exclusive disjunction, respectively.

For modular types, the predefined logical operators are defined on a
bit-by-bit basis, using the binary representation of the value of
the operands to yield a binary representation for the result, where
zero represents False and one represents True [...]

[...]

Cette dernière paragraphe me semble exactement la définition des
opérateurs &, | et ^ en C++. Y compris la mauvaise précédence:-).

Il y a aussi un « not » dans §4.5.6, avec des sémantiques (pour un type
« modular ») qui correspond à ! en C++.

En revanche, je ne trouve pas de décalages, même dans les fonctions
prédéfinies (Appendice A). Mais peut-être je ne sais pas où chercher.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Jean-Marc Bourguet
writes:

Cette dernière paragraphe me semble exactement la définition des
opérateurs &, | et ^ en C++. Y compris la mauvaise précédence:-).


Euh, la precedence des operateurs en Ada ne depend pas des types
utilises. Et le and booleen ou le and binaire, c'est le meme
operateur. Par contre si j'ai bonne memoire, and et or ont la meme
precedence et pas de priorite (autrement dit les parentheses sont
obligatoires quand on les melange). Mais je parlais des decalages,
pas des autres operations.

Il y a aussi un « not » dans §4.5.6, avec des sémantiques (pour un
type « modular ») qui correspond à ! en C++.


Non, le not correspond a ~ (a moins qu'il y en ai aussi un qui
correspond a ! ce qui est possible avec la surcharge sur le type
resultat des fonctions/operateurs mais j'en doute, ce n'est pas
tellement dans la philosophie Ada). Les types modular ont globalement
la meme semantique que les unsigned en C++ (a part qu'on peut s'amuser
a avoir des modular sur autre chose que les puissances de 2, mais la
le comite s'est amuse). Il y a Natural pour la semantique que
certains aimeraient donner a unsigned :-)

En revanche, je ne trouve pas de décalages, même dans les fonctions
prédéfinies (Appendice A). Mais peut-être je ne sais pas où
chercher.


C'est donc mon souvenir (si j'ai bonne memoire, gnat a des fonctions
intrinseques pour ca et peut-etre meme les rotations, mais ce n'est
pas standard).

Naturellement, un moyen naturel en Ada (le seul disponible en Ada83;
en Ada95 il y a en plus les modular) de manipuler des bits, c'est
d'utiliser des tableaux (compactes) de booleens. Auquel cas la
concatenation de tableaux et l'utilisation d'intervalles permettent
d'exprimer les operations naturellements (et c'est vraissemblablement
des idiomes reconnus pas les compilateurs -- j'ai jamais verifie en
Ada mais les synthetiseurs VHDL le font).

Par exemple
A = A(A'Low) & A(A'Hight .. A'Low+1);
pour une rotation.

(Euh, si on continue comme ca, il va falloir mettre fcla en copie, ne
fut-ce que pour corriger ma memoire.)

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Fabien LE LEZ
On 29 Jun 2004 21:30:47 GMT, "Michaël Delva"
:

A ce propos, vous auriez de la doc sur ces symboles?


N'importe quel bouquin de C ou de C++ (voire d'autres langages)
devrait en contenir la liste exhaustive, non ?
Ainsi d'ailleurs que la doc de la plupart des compilos...

Pour le cas particulier de &, | et ^, soit tu as l'occasion de
travailler sur les bits, et tu sais comment ça marche, soit tu ne
travailles jamais directement sur les bits, et tu peux les ignorer.


--
schtroumpf schtroumpf

1 2 3 4