Je pensais comment obtenir la valeur absolue d'un entier sans utiliser l'instruction if
ni abs()
. Au début, j’utilisais les bits de décalage restants (<<
) pour essayer d’obtenir un signe négatif en dehors de la plage, puis de décaler les bits exactement là où il se trouvait, mais malheureusement, cela ne fonctionne pas pour moi. S'il vous plaît laissez-moi savoir pourquoi cela ne fonctionne pas et d'autres moyens alternatifs de le faire.
De Bit Twiddling Hacks :
int v; // we want to find the absolute value of v
unsigned int r; // the result goes here
int const mask = v >> sizeof(int) * CHAR_BIT - 1;
r = (v + mask) ^ mask;
int abs(int v)
{
return v * ( (v<0) * (-1) + (v>0));
// simpler: v * ((v>0) - (v<0)) thanks Jens
}
Ce code multiplie la valeur de v
par -1
ou 1
pour obtenir abs (v). Par conséquent, entre parenthèses se trouvera l'un des -1
ou 1
.
Si v
est positif, l'expression (v>0)
est vraie et aura la valeur 1
tant que (v<0)
sera faux (avec la valeur 0 pour faux). Par conséquent, lorsque v
est positif ((v>0) - (v<0)) = (1-0) = 1
. Et toute l'expression est: v * (1) == v
.
Si v
est négatif, l'expression (v>0)
est false et aura la valeur 0
tant que (v<0)
est vrai (valeur 1). Ainsi, pour v
négatif, ((v>0) - (v<0)) = (0-1) = -1
. Et toute l'expression est: v * (-1) == -v
.
Lorsque v == 0
, (v<0)
et (v>0)
seront évalués à 0, laissant: v * 0 == 0
.
Sans branches*:
int abs (int n) {
const int ret[2] = { n, -n };
return ret [n<0];
}
Note 4.7 Conversions intégrales/4: [...] If the source type is bool, the value false is converted to zero and the value true is converted to one.
*: En ce sens qu'il n'y a pas de branchement conditionnel dans votre code. Sous le capot, l’opérateur ternaire produira également une succursale. Cependant, c'est aussi une réponse valable, car la réponse ternaire n'est pas une instruction if. Cela ne signifie pas que votre compilateur n'est pas capable d'émettre du code Assembly sans branche pour le code qui se branche de manière logique.
En supposant des entiers signés 32 bits (Java), vous pouvez écrire:
public static int abs(int x)
{
return (x + (x >> 31)) ^ (x >> 31);
}
Pas de multiplication, pas de branche.
BTW, return (x ^ (x >> 31)) - (x >> 31);
fonctionnerait aussi bien mais il est breveté. Ouaip!
Remarque: Ce code peut prendre plus de 10 fois plus longtemps qu'une instruction conditionnelle (version 8 bits). Cela peut être utile pour la programmation matérielle Système C, etc.
Décalage binaire des entiers signés selon votre manière de considérer est un comportement indéfini et n'est donc pas une option. Au lieu de cela, vous pouvez faire ceci:
int abs(int n) { return n > 0 ? n : -n; }
Aucune instruction if
, il ne s'agit que d'une expression conditionnelle.
J'essaye ce code en C, et ça marche.
int abs(int n){
return n*((2*n+1)%2);
}
J'espère que cette réponse vous sera utile.
Voici une autre approche sans abs()
, ni aucune expression logique/conditionnelle: Suppose que int est un entier de 32 bits ici. L'idée est assez simple: (1 - 2 * sign_bit)
convertira sign_bit = 1 / 0 to -1 / 1
.
unsigned int abs_by_pure_math( int a ) {
return (1 - (((a >> 31) & 0x1) << 1)) * a;
}
Je n'ai pas vu celui-ci. Pour la représentation du complément à deux et int 32 bits
( n >> 31 | 1 ) * n
Essayez ce qui suit:
int abs(int n)
{
return sqrt(n*n);
}
Si votre langage autorise bool à intégrer (comme C/C++)
float absB(float n) {
return n - n * 2.0f * ( n < 0.0f );
}
Pas de branches ni de multiplication:
int abs(int n) {
int mask = n >> 31;
return (mask & -n) | (~mask & n);
}
Qu'en est-il de celui-ci:
#include <climits>
long abs (int n) { // we use long to avoid issues with INT MIN value as there is no positive equivalents.
const long ret[2] = {n, -n};
return ret[n >> (sizeof(int) * CHAR_BIT - 1)]; // we use the most significant bit to get the right index.
}
Utilisez l'opérateur ternaire:
y = condition ? value_if_true : value_if_false;
Il y a plusieurs raisons de déplacer le bit de signe vers l'extérieur et de revenir à la droite (v << 1 >> 1
):
unsigned
aurait l'effet souhaité: (unsigned)v << 1 >> 1
supprime le bit de signe s'il n'y a pas de bits de remplissage, mais la valeur résultante est la valeur absolue de v
uniquement sur les systèmes avec une représentation signe + magnitude, qui sont extrêmement rares aujourd'hui. Sur l'architecture du complément 2 omniprésent, la valeur résultante pour v
négatif est INT_MAX+1-v
La solution de Hasturkun a malheureusement un comportement défini par la mise en œuvre.
Voici une variante entièrement définie pour les systèmes avec représentation du complément à 2 pour les valeurs signées:
int v; // we want to find the absolute value of v
unsigned int r; // the result goes here
unsigned int mask = -((unsigned int)v >> (sizeof(unsigned int) * CHAR_BIT - 1));
r = ((unsigned int)v + mask) ^ mask;
que diriez-vous de cela:
value = value > 0 ? value: ~value + 1
elle repose sur le fait que les nombres négatifs sont stockés en tant que complément à 2 à leur équivalent positif, et que l'on peut construire le complément à 2 en construisant d'abord le complément à 1 et en ajoutant 1, donc
5 -> 0000 0101b
-5 -> (1111 1010b) + 1 -> 1111 1011b
ce que j'ai fait était essentiellement d'inverser cette tendance,
-5 -> 1111 1011b
5 -> (0000 0100b) + 1 -> 0000 0101b
Je sais que c'est un peu tard, mais le problème est le même et j'ai atterri ici. J'espère que cela aidera.