web-dev-qa-db-fra.com

obtenir une valeur absolue sans utiliser la fonction abs ni l'instruction if

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. 

49
shanwu

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;
42
Hasturkun
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.

24
perreal

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.

21
Sebastian Mach

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.

8
flanglet

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.

3
Kerrek SB

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;
}
2
dewang

Je n'ai pas vu celui-ci. Pour la représentation du complément à deux et int 32 bits

( n >> 31 | 1 ) * n
2
MAG

Essayez ce qui suit:

int abs(int n) 
{
  return sqrt(n*n);
}
2
Jeremy

Si votre langage autorise bool à intégrer (comme C/C++)

float absB(float n) {
    return n - n * 2.0f * ( n < 0.0f );
}
1

Pas de branches ni de multiplication:

int abs(int n) {
    int mask = n >> 31;
    return (mask & -n) | (~mask & n);
}
0
TurplePurtle

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.
}
0
Antonin GAVREL

Utilisez l'opérateur ternaire:

y = condition ? value_if_true : value_if_false;
0

Il y a plusieurs raisons de déplacer le bit de signe vers l'extérieur et de revenir à la droite (v << 1 >> 1):

  • le décalage gauche d'un type signé avec une valeur négative a un comportement indéfini, il ne doit donc pas être utilisé du tout.
  • attribuer la valeur à 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;
0
chqrlie

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.

0
Martin