7
votes

Comment éviter de ramification en C pour cette opération

Y a-t-il un moyen de supprimer l'instruction IF-IF suivante pour vérifier si la valeur est inférieure à 0? xxx pré>

La valeur de C code> devrait mentir entre 0 et 3600. Les deux A code> et B code> sont signés. La valeur du A code> doit également se situer entre 0 et 3600. (Oui, il s'agit d'une valeur de comptage dans 0,1 degrés). La valeur est réinitialisée par une interruption à 3600, mais si cette interruption est trop tardive, elle ne pose pas de problème, mais le logiciel doit toujours être en mesure de le gérer. Ce qu'il fait. P>

Nous faisons ce si (C Vérifiez à des endroits assez certains où nous calculons des positions. (Calculer une nouvelle position, etc.) P>

J'ai été habitué à Python Modulo Opérateur d'utiliser la signature du diviseur où notre compilateur (C89) utilise la signature de dividendes. P>

est là un moyen de faire ce calcul différemment? Exemple de résultats: P>

 a  -  b  = c
100 - 200 = 3500  
200 - 100 = 100


6 commentaires

Pourquoi es-tu inquiet pour la branche? L'alternative est quelque chose comme ((a - b) + 3600)% 3600 . Cela suppose A et b se trouvent dans la plage 0..3600 déjà; S'ils ne sont pas sous contrôle, la solution la plus générale est celle de McGowen suggère: ((A - B)% 3600 + 3600)% 3600`. La branche Miss doit être très coûteuse pour faire autant de calcul.


Une astuce que j'avais l'habitude d'utiliser serait ((a - b)% 3600 + 3600)% 3600 - cependant avec deux opérateurs de modulus, vous pourriez effectivement mieux à utiliser la comparaison


@Jonathanleffler Nous avons un vieux compilateur mauvais mais optimisé. Il génère des branches gonflées. Je viens de vérifier vos réponses, chacun produit des instructions de montage et un code plus facile à maintenir. Si vous pouviez vous fournir les deux une réponse, je peux vous donner un uppote et accepter Jonathan. Je n'ai pas besoin de 2 ops Module. Les deux valeur A et b sont sous notre propre contrôle.


Sent comme une optimisation prématurée pour moi. Si vous avez une CPU qui prend en charge des mouvements conditionnels ou d'autres alternatives pour générer un code sans succursale et un compilateur décemment moderne qui prend en charge la CPU, il risque de générer un code sans succursale pour vous, même si vous utilisez si (...) { } ... En outre, l'opérateur MOD pourrait être plus lent qu'une branche courte du code qui est probablement déjà en cache ...


Il génère également moins de cycles.


@Twalberg Ce n'est pas le cas. Old Fujitsu Compilateur. Même le microprocesseur plus ancien. Pas de mouvements conditionnels, pas de choses à picité fantaisie. Sauf pour un diviseur matériel


5 Réponses :


5
votes

Pourquoi êtes-vous inquiet pour la branche? [raison expliquée dans les commentaires à la question.] em>

L'alternative est quelque chose comme: p> xxx pré>

Ceci suppose A code > et b code> sont dans la plage 0..3600 code> déjà; S'ils ne sont pas sous contrôle, la solution plus générale est celle de Drew McGowen Suggest : P>

((a - b) % 3600 + 3600) % 3600


0 commentaires

11
votes

bonne question! Que dis-tu de ça?

c += 3600 * (c < 0);


1 commentaires

@Abelenky Non, les valeurs de vérité sont zéro / non nulle, mais les opérateurs de comparaison ne renvoient que zéro ou un.



4
votes

@skjaidev a montré comment le faire sans ramification. Voici comment éviter automatiquement la multiplication, aussi bien lorsque INT code> S est TwoS-Complément:

#if ((3600 & -0) == 0) && ((3600 & -1) == 3600)
c += 3600 & -(c < 0);
#else
c += 3600 * (c < 0);
#endif


0 commentaires

7
votes

Qu'en est-il de cela (en supposant des INT de 32 bits): xxx

c >> 31 définit tous les bits sur le MSB d'origine, qui est 1 pour les nombres négatifs et et 0 pour d'autres personnes en 2-complément.

La droite du numéro de numéro négatif est formellement définie par la mise en oeuvre conformément aux documents standard de C, mais il est presque toujours mis en œuvre avec la copie du MSB (les processeurs courants peuvent le faire dans une seule instruction. ).

Cela n'entraînera certainement aucune branche, contrairement à (C <0) qui pourrait être mis en œuvre avec une branche dans certains cas.


5 commentaires

Très agréable. Mais vous devriez annuler (C >> 31) avant de l'ajouter à 3600.


@ Sát Non, je veux avoir un masque complet si le nombre est négatif (MSB est 1). Je veux zéro si le nombre est non négatif.


Euh, assez vrai, désolé pour ça: $.


Pour les entiers signés, il est défini la mise en œuvre de savoir si vous obtiendrez le bit de signalisation répliqué. ISO / IEC 9899: 2011 §6.5.7 Opérateurs de décalage des bits: ¶5 Le résultat de E1 >> E2 est E1 droite décalé E2 E2 Positions de bits. Si E1 a un type non signé ou si E1 a un type signé et une valeur non négative, la valeur du résultat fait partie intégrante du quotient de E1 / 2 ** e2 [exponentiation; ne peut pas faire de superscripts dans les commentaires] . Si E1 a un type signé et une valeur négative, la valeur résultante est définie par la mise en œuvre.


Bien qu'une belle solution, cela n'est tristement pas autorisé selon nos malaques que nous devons nous conformer. Il est possible de le rendre conforme mais entraîne de nombreuses moulages laids.



0
votes

Ce que vous voulez faire est l'arithmétique modulaire. Votre machine de complément de 2 est déjà en integer Math. Donc, en mappant vos valeurs dans le complément de 2 arithmétiques, vous pouvez obtenir le fonctionnement Modolo gratuitement.

Le truc est de représenter votre angle comme une fraction de 360 ​​degrés compris entre 0 et 1-Epsilon. Bien sûr, alors vos angles constants devraient être représentés de la même manière, mais cela ne devrait pas être difficile; c'est juste un peu de mathématiques que nous pouvons nous cacher dans une fonction de conversion (ER, macro). P>

La valeur de cette idée est que si vous ajoutez ou soustrayez des angles, vous obtiendrez une valeur dont la fraction vous Vous voulez, et quelle partie entière vous voulez jeter. Si nous représentons la fraction en tant que numéro de point fixe de 32 bits avec le point binaire à 2 ^ 32 (par exemple, à gauche de ce qui est normalement considéré comme un bit de signe), les débordements de la fraction tombent tout simplement sur le dessus du Valeur 32 bits gratuitement. Donc, vous faites toutes les mathématiques entier et «débordement» se passe gratuitement. P>

Donc, je réécris votre code (préservant l'idée de degrés Times 10): P>

  typedef unsigned int32 angle; // angle*3600/(2^32) represents degrees
  #define angle_scale_factor 1193046.47111111 // = 2^32/3600
  #define make_angle(degrees)  (unsigned int32)((degrees%3600)*angle_scale_factor ) 
  #define make_degrees(angle) (angle/(angle_scale_factor*10)) // produces float number

  ...

  angle a = make_angle(100);  // compiler presumably does compile-time math to compute 119304647 
  angle b = make_angle(200);  // = 238609294
  angle c = a - b; // compiler should generate integer subtract, which computes 4175662649

  #if 0 // no need for this at all; other solutions execute real code to do something here
  if (c < 0)  // this can't happen
     { c += 3600; } // this is the wrong representation for our variant
  #endif


  // speed doesn't matter here, we're doing output:
  printf("final angle %f4.2 = \n", make_degrees(c)); // should print 350.00


0 commentaires