OVH Cloud OVH Cloud

Decalages d'un nombre de bits pouvant etre >= a la taille du type

134 réponses
Avatar
Vincent Lefevre
J'ai besoin de faire un décalage vers la droite sur un type entier
(disons non signé) générique (i.e. défini par typedef) d'un certain
nombre constant de bits. Le problème est que ce nombre de bits peut
être supérieur ou égal à la taille du type en question. Une idée
sur la façon de faire ça efficacement sans obtenir de comportement
indéfini et sans passer par un outil du style "configure"?

--
Vincent Lefèvre <vincent@vinc17.org> - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / SPACES project at LORIA

10 réponses

1 2 3 4 5
Avatar
Jean-Marc Bourguet
Vincent Lefevre <vincent+ writes:

J'ai besoin de faire un décalage vers la droite sur un type entier
(disons non signé) générique (i.e. défini par typedef) d'un certain
nombre constant de bits. Le problème est que ce nombre de bits peut
être supérieur ou égal à la taille du type en question. Une idée
sur la façon de faire ça efficacement sans obtenir de comportement
indéfini et sans passer par un outil du style "configure"?


Pourquoi est-ce que l'evident

#define SAVE_RSHIFT(v, c) ((sizeof(v) * CHAR_BIT >= (c)) ? 0 : (v) >> (c))

ne te convient pas? Si le compilateur n'est pas trop con, il va
simplifier cela comme il faut pour des decalages constants, sinon
est-ce que sa qualite ne serait pas telle que l'optimisation manquante
est de toute maniere sans objet?

OK, on suppose qu'il n'y a pas de bits de padding dans les entiers non
signes. C'est quoi encore la derniere machine avec une telle
caracteristique? La lisp-machine?

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Laurent Deniau
Jean-Marc Bourguet wrote:
Vincent Lefevre <vincent+ writes:


J'ai besoin de faire un décalage vers la droite sur un type entier
(disons non signé) générique (i.e. défini par typedef) d'un certain
nombre constant de bits. Le problème est que ce nombre de bits peut
être supérieur ou égal à la taille du type en question. Une idée
sur la façon de faire ça efficacement sans obtenir de comportement
indéfini et sans passer par un outil du style "configure"?



Pourquoi est-ce que l'evident

#define SAVE_RSHIFT(v, c) ((sizeof(v) * CHAR_BIT >= (c)) ? 0 : (v) >> (c))


le nom peut preter a confusion, SAFE_RSHIFT conviendrait mieux non?

Ceci dit, comme je l'ai dit dans mon post precedent, tu aura quand meme
un warning du compilo (GCC: warning: right shift count >= width of
type). C'est pour ca que l'on utilise generalement les UXXX_MAX et les
directives du preprocesseur pour selectionner le code qui va bien.

a+, ld.


Avatar
Vincent Lefevre
Dans l'article <cju8hj$dnm$,
Laurent Deniau écrit:

Au passage, tu ne nous as toujours pas explique pourquoi tu tiens
absolument a faire les decalages?


J'accepte tout code équivalent.

unsigned long U = T;

if (ULONG_MAX == 4294967295UL && GMP_LIMB_BITS > 31) {
U = 0;
} else {
U >>= GMP_LIMB_BITS;
}

T = U

est quand meme plus simple et defini.


Non, pas si GMP_LIMB_BITS est supérieur à la taille d'un unsigned long.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / SPACES project at LORIA

Avatar
Vincent Lefevre
Dans l'article ,
Jean-Marc Bourguet écrit:

Vincent Lefevre <vincent+ writes:

J'ai besoin de faire un décalage vers la droite sur un type entier
(disons non signé) générique (i.e. défini par typedef) d'un certain
nombre constant de bits. Le problème est que ce nombre de bits peut
être supérieur ou égal à la taille du type en question. Une idée
sur la façon de faire ça efficacement sans obtenir de comportement
indéfini et sans passer par un outil du style "configure"?


Pourquoi est-ce que l'evident

#define SAVE_RSHIFT(v, c) ((sizeof(v) * CHAR_BIT >= (c)) ? 0 : (v) >> (c))

ne te convient pas?


Comportement indéfini.

OK, on suppose qu'il n'y a pas de bits de padding dans les entiers
non signes. C'est quoi encore la derniere machine avec une telle
caracteristique? La lisp-machine?


Si cela n'a pas d'importance, pourquoi ça n'a pas été supprimé de la
norme alors?

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / SPACES project at LORIA


Avatar
Jean-Marc Bourguet
Laurent Deniau writes:

#define SAVE_RSHIFT(v, c) ((sizeof(v) * CHAR_BIT >= (c)) ? 0 : (v) >> (c))


le nom peut preter a confusion, SAFE_RSHIFT conviendrait mieux non?


Oui :-)

Ceci dit, comme je l'ai dit dans mon post precedent, tu aura quand
meme un warning du compilo (GCC: warning: right shift count >= width
of type).


J'ai manque ca. :-(

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Jean-Marc Bourguet
Vincent Lefevre <vincent+ writes:

Pourquoi est-ce que l'evident

#define SAVE_RSHIFT(v, c) ((sizeof(v) * CHAR_BIT >= (c)) ? 0 : (v) >> (c))

ne te convient pas?


Comportement indéfini.


C'est les bits de padding qui te genent ou il y a qqch que je n'ai pas
vu?

OK, on suppose qu'il n'y a pas de bits de padding dans les entiers
non signes. C'est quoi encore la derniere machine avec une telle
caracteristique? La lisp-machine?


Si cela n'a pas d'importance, pourquoi ça n'a pas été supprimé de la
norme alors?


J'etais pas la, je ne peux donc te donner que des suppositions qui
vont de personne n'a trouve le probleme suffisemment important pour en
faire une proposition formelle a il y a une machine repandue que je ne
connais pas sur laquelle cela pose un probleme.

La norme est un moyen, pas une fin.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Laurent Deniau
Vincent Lefevre wrote:
Dans l'article <cju8hj$dnm$,
Laurent Deniau écrit:


Au passage, tu ne nous as toujours pas explique pourquoi tu tiens
absolument a faire les decalages?



J'accepte tout code équivalent.


unsigned long U = T;



if (ULONG_MAX == 4294967295UL && GMP_LIMB_BITS > 31) {
U = 0;
} else {
U >>= GMP_LIMB_BITS;
}



T = U



est quand meme plus simple et defini.



Non, pas si GMP_LIMB_BITS est supérieur à la taille d'un unsigned long.


Defini dans tous les cas mais ne fait pas necessairement ce que tu veux
en >32 bits d'ou ma suggestion initiale de passer par le preprocesseur
pour selectionner le code correct. Idem pour le warning du compilo.

Le probleme n'est pas si GMP_LIMB_BITS shift plus que la taille d'un
unsigned long mais si le type de T est superieur la taille d'un unsigned
long.

Si tu ne trouves toujours pas, je te posterais le code.

a+, ld.


Avatar
Vincent Lefevre
Dans l'article <cjucbf$lub$,
Laurent Deniau écrit:

Defini dans tous les cas mais ne fait pas necessairement ce que tu veux
en >32 bits d'ou ma suggestion initiale de passer par le preprocesseur
pour selectionner le code correct. Idem pour le warning du compilo.


Non, la norme dit bien que c'est du comportement indéfini.

6.5.7 Bitwise shift operators
[...]
[#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.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / SPACES project at LORIA

Avatar
Vincent Lefevre
Dans l'article ,
Jean-Marc Bourguet écrit:

Vincent Lefevre <vincent+ writes:

Pourquoi est-ce que l'evident

#define SAVE_RSHIFT(v, c) ((sizeof(v) * CHAR_BIT >= (c)) ? 0 : (v) >> (c))

ne te convient pas?


Comportement indéfini.


C'est les bits de padding qui te genent ou il y a qqch que je n'ai pas
vu?


Non, c'est le 6.5.7#3.

J'etais pas la, je ne peux donc te donner que des suppositions qui
vont de personne n'a trouve le probleme suffisemment important pour en
faire une proposition formelle a il y a une machine repandue que je ne
connais pas sur laquelle cela pose un probleme.

La norme est un moyen, pas une fin.


Tu fais ce que tu veux. En ce qui me concerne, je n'ai pas du tout
envie de revoir mon code dès qu'une nouvelle machine apparaît.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / SPACES project at LORIA



Avatar
Charlie Gordon
La macro évidente :

#define SAFE_RSHIFT(v, c) ((sizeof(v)*CHAR_BIT >= (c)) ? 0 : (v) >> (c))

a plusieurs inconvénients :
- la comparaison est faite à l'envers, ce qui est facile à corriger ;-)
- c est instancié deux fois.
- on a potentiellement un comportement indéfini sur une partie de
l'expression.
- on a un warning stupide dans ce cas si c est constant et que le
compilateur se croit malin.
- on peut aussi avoir un warning si c est une expression d'un type signé à
cause de la comparaison signé/non-signé
- le code généré est inefficace si c est une expression non constante (2
jump)

On peut remédier à ces inconvénients comme ceci:
- on utilise une fonction inline au lieu d'une macro, mais cela force le
type de v, ce qui n'est pas forcement loisible
- on peut masquer c dans le shift, ce qui supprime le warning sans changer
le code pour les valeurs constantes de c, mais ralentit un peu les instances
ou c est non constante, à moins que le compilo soit très fort et que
l'architecture s'y prete. On peut aussi utiliser 2 macros differentes pour
les shifts constants et calculés.
- on caste (c) en (unsigned)
- on peut faire une expression sans test, ce qui generera un meilleur code
si le compilo est subtil.

Exemples:

// code avec jmp:
#define SAFE_SHR(v, c) ((sizeof(v) * CHAR_BIT < (unsigned)(c)) ? 0 : (v) >>
((c) & (sizeof(v) * CHAR_BIT - 1)))
// code sans jmp pour les valeurs constantes ou non constantes:
#define SAFE_SHR(v, c) (-(sizeof(v) * CHAR_BIT < (unsigned)(c)) & ((v) >>
((c) & (sizeof(v) * CHAR_BIT - 1)))

On peut supprimer le masquage dans la version non constante si un shift
d'une quantité trop grande ne déclenche pas d'exception.
On devrait caster (c) en (unsigned) dans la comparaison pour éviter un
warning potentiel sur la comparaison d'un signé et d'un non-signé.
Pour ceux qui ont un doute sur la facon de supprimer le jump, on peut aussi
écrire cela, mais c'est encore moins lisible: .
#define SAFE_SHR(v, c) ((((int)c - (int)sizeof(v) * CHAR_BIT) >>
(sizeof(int) * CHAR_BIT - 1)) & ((v) >> ((c) & ((sizeof(v) * CHAR_BIT) -
1)))

Ceci suppose que le shift signé duplique le bit de signe. Donc à n'utiliser
que sur les architectures où cette supposition est valide
Un bon gros commentaire sera indispensable pour expliquer ce que font ces
macros !

Vite une aspirine !

Chqrlie.

PS: je trouve regrettable que le shift d'une quantité *constante* supérieure
à la taille du type non soit pas défini avec la sémantique évidente, mais je
suppose que c'est pour préserver la cohérence et l'efficacité de
l'implémentation.
1 2 3 4 5