[C] décalage par un nombre négatif

Résolu/Fermé
Shinjitm Messages postés 13 Date d'inscription vendredi 13 février 2009 Statut Membre Dernière intervention 9 novembre 2011 - 13 juin 2011 à 19:21
Shinjitm Messages postés 13 Date d'inscription vendredi 13 février 2009 Statut Membre Dernière intervention 9 novembre 2011 - 14 juin 2011 à 13:51
Bonjour,

Je cherche à réaliser, en C, l'opération suivante sur des entiers (signés) :

a = b * 2^(c - d);

Problème : je travaille sur du temps-réel, et ce type d'opération peut être longue. Je pensais donc simplement utiliser des décalages à droite ou à gauche. Cependant, plus rien ne semble marcher lorsque c est inférieur à d : l'opération a = b >> (c-d); me rend toujours 0 dans ce cas là. Idéalement, j'aurais voulu que, par exemple b >> (-2) réalise l'opération b << 2.

Est-ce possible ? le C autorise-t-il ce genre de "fantaisies" ?

D'avance merci
Shinjitm

4 réponses

KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
13 juin 2011 à 20:09
Pourquoi ne pas faire tout simplement ceci ?

a = (c<d) ? b << (d-c) : b >> (c-d);
1
Shinjitm Messages postés 13 Date d'inscription vendredi 13 février 2009 Statut Membre Dernière intervention 9 novembre 2011
13 juin 2011 à 20:37
J'aimerais, autant que possible, éviter d'utiliser des conditions. Tous les branchements conditionnels sont consommateurs de temps de calcul ("vidage" du pipeline du processeur), ce qui est assez coûteux quand on fait du temps-réel. Un décalage est une opération de processeur, un if peut en coûter une dizaine.
0
KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
13 juin 2011 à 20:43
Moins coûteux tu vas avoir ce code, mais il va réduire l'amplitude des valeurs utilisables.
En effet si tu utilises par exemple c=d très grands, tu vas déborder et perdre de la précision. Après ça peut dépendre des conditions que tu as sur c et d...

a = (b << d) >> c
0
Shinjitm Messages postés 13 Date d'inscription vendredi 13 février 2009 Statut Membre Dernière intervention 9 novembre 2011
13 juin 2011 à 20:49
Bon sang, c'est simple, c'est si évident ... C'est parfait.
Merci !!
0
KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
13 juin 2011 à 20:51
Mais n'oublie pas de prendre en compte ma remarque, sinon tu vas avoir des problèmes !
0
Shinjitm Messages postés 13 Date d'inscription vendredi 13 février 2009 Statut Membre Dernière intervention 9 novembre 2011
14 juin 2011 à 10:28
Je le ferai

L'opération à réaliser est la suivante :
int a;
int b;
int c;
unsigned int A;
unsigned int B;
unsigned int C;
c = (int)(((long) a * (long) b) >> (A + B - C);

Dans de nombreux cas, ça doit passer sans soucis. En se creusant un peu la cervelle, ça marchera, je ne m'en fais pas trop.

Encore merci !
0
KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
14 juin 2011 à 10:51
Tu parlais d'efficacité en rechignant sur ma condition trop coûteuse, mais faire 3 casts comme tu le fais me paraît bien plus coûteux !

Puisque tu diminues les puissances (avec >>) je ne penses pas que tu ais besoin de faire un cast sur un type plus grand, et du coup ta multiplication sera moins coûteuse si tu diminues tes puissances avant...

En plus il me parait évident que A, B et C ne devrons pas dépasser sizeof(int) sinon il y aura dépassement de capacité, alors les mettre en unsigned int me paraît erroné...

int a, b, c;
unsigned char A,B,C;

c = ( (a >> A) * (b >> B) ) << C;
0
Shinjitm Messages postés 13 Date d'inscription vendredi 13 février 2009 Statut Membre Dernière intervention 9 novembre 2011
14 juin 2011 à 11:19
J'avoue que je ne connais pas le coût, en terme de temps de calcul, d'un cast. Si tu as une information la dessus, je suis preneur.

Cela dit, je travaille actuellement sur des processeurs 32bits. Actuellement, j'utilise une librairie mathématique fournie par le fabriquant qui réalise exactement l'opération que je souhaite, mais sur des entiers 32 bits castés en 64 bits pour l'opération. Je cherche donc un moyen de réaliser l'opération en 16 / 32 bits plutôt que 32 / 64, soit pour gagner du temps de calcul, soit pour pouvoir passer sur une famille de microprocesseurs moins puissants.

Le calcul en question est de la "virgule fixe". C'est à dire que si je décrète arbitrairement que j'ai "4 bits après la virgule", le chiffre 1 est représenté par 8. Ce qui me permet d'avoir des "entiers" qui "valent" 0,125.
Avec la librairie actuelle, toutes les variable ont le même nombre de "bits après la virgule", ce qui simplifie beaucoup les calculs. Mais en passant en 16 bits, travailler de cette façon réduit considérablement l'excursion de valeurs possibles. D'où l'idée de travailler sur des paires {a,A}, a étant ma vraie variable et A le "nombre de bits après la virgule". Chaque nombre a ainsi une plage d'excursion et une précision adaptées à son utilisation.

Ta nouvelle solution, dans ce cadre, pose le problème de la précision. La précision finale souhaitée pour c est C. Si je fais le décalage de a et b avant la multiplication, je perd immédiatement tous les "chiffres après la virgule", donc beaucoup de précision, dans l'opération. Il faut donc commencer par faire la multiplication, puis le décalage dans lequel je ne garde que la précision qui m'intéresse.

Cela dit, je note ta remarque en ce qui concerne le type de A, B, C.
0
KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
14 juin 2011 à 11:53
Sur 16 bits : signed short int représente 1 valeur
1 bit de signe, 11 bits avant la virgule et 4 bits après la virgule.

Sur 32 bits : signed long int représente le produit de 2 valeurs
1 bit de signe, 1 bit inutile, 22 bits avant la virgule et 8 bits après la virgule.

Pour faire la multiplication de deux valeurs sur 16 bits, tu devras prendre un résultat sur 32 bits, et faire la multiplication. Là tu fais >>4 pour supprimer les 4 bits de précisions en trop et tu castes en 16 bits.

inline signed short int multiplier(
                       const signed short int a  // 16 bits (précision 4 bits)
                       const signed short int b) // 16 bits (précision 4 bits)
{
     signed long int c = a; // 32 bits (précision 4 bits)
     c *= b    // 32 bits (précision 8 bits)
     c >>= 4;  // 32 bits (précision 4 bits)
     return c; // 16 bits (précision 4 bits)
}
0