web-dev-qa-db-fra.com

Comment calculer la valeur absolue entière

Comment calculer la valeur absolue entière sans utiliser la condition if. Je pense que nous devons utiliser une opération au niveau des bits .. Quelqu'un peut-il aider?

26
Pragyan

1) Définissez le masque comme décalage à droite de l'entier par 31 (en supposant que les entiers sont stockés en tant que valeurs 32 bits complémentaires de deux et que l'opérateur de décalage à droite signe l'extension en conséquence).

 mask = n>>31 

2) XOR le masque avec numéro

mask ^ n 

3) Soustrayez le masque du résultat de l'étape 2 et renvoyez le résultat.

(mask^n) - mask 
25
Mady

Identique aux réponses existantes, mais avec plus d'explications:

Supposons un nombre à deux (comme c'est le cas habituel et vous ne dites pas le contraire) et supposons que 32 bits:

Tout d'abord, nous effectuons un décalage arithmétique à droite de 31 bits. Cela déplace tous les 1s pour un nombre négatif ou tous les 0s pour un nombre positif (mais notez que le comportement réel de l'opérateur >>- en C ou C++ est défini par une implémentation définie pour les nombres négatifs, mais qu'il effectue normalement un décalage arithmétique, mais supposons simplement que pseudocode ou des instructions matérielles réelles, car cela ressemble quand même à un devoir):

mask = x >> 31;

Donc, ce que nous obtenons est 111...111 (-1) pour les nombres négatifs et 000...000 (0) pour les positifs

Maintenant, nous XOR avec x, obtenant le comportement d'un NOT pour mask=111...111 (négatif) et d'un non-fonctionnement pour mask=000...000 (positif):

x = x XOR mask;

Et enfin soustrayez notre masque, ce qui signifie +1 pour les négatifs et + 0/non-op pour les positifs:

x = x - mask;

Donc, pour les positifs, nous effectuons un XOR avec 0 et une soustraction de 0 et obtenons ainsi le même nombre. Et pour les négatifs, nous avons obtenu (NOT x) + 1, qui est exactement -x lorsqu’on utilise une représentation à deux compléments.

71
Christian Rau

Supposons que int est de 32 bits.

int my_abs(int x)
{
    int y = (x >> 31);
    return (x ^ y) - y;
}
6
timrau

On peut aussi effectuer l'opération ci-dessus comme:

return n*(((n>0)<<1)-1);

n est le nombre dont le nombre absolu doit être calculé.

2
P.R.

J'ai écrit le mien avant de découvrir cette question.

Ma réponse est probablement plus lente, mais toujours valable:

int abs_of_x = ((x*(x >> 31)) | ((~x + 1) * ((~x + 1) >> 31)));
1
abelenky

En C, vous pouvez utiliser des unions pour effectuer des manipulations de bits sur des doubles. Ce qui suit fonctionnera en C et peut être utilisé pour les entiers, les flottants et les doubles.

/**
* Calculates the absolute value of a double.
* @param x An 8-byte floating-point double
* @return A positive double
* @note Uses bit manipulation and does not care about NaNs
*/
double abs(double x)
{
    union{
        uint64_t bits;
        double dub;
    } b;

    b.dub = x;

    //Sets the sign bit to 0
    b.bits &= 0x7FFFFFFFFFFFFFFF;

    return b.dub;
}

Notez que cela suppose que les doubles sont de 8 octets.

1
Sheldon Juncker

Dans C #, vous pouvez implémenter abs () sans utiliser de variables locales:

public static long abs(long d) => (d + (d >>= 63)) ^ d;

public static int abs(int d) => (d + (d >>= 63)) ^ d;


Remarque: en ce qui concerne0x80000000 (int.MinValue)et0x8000000000000000 (long.MinValue):

Comme avec toutes les autres méthodes binaires/sans branches montrées sur cette page, cela donne le résultat unique non mathématique abs(int.MinValue) == int.MinValue (de même pour long.MinValue). Ils représentent les uniquement cas où la valeur du résultat est négative, c’est-à-dire où le MSB du complément à deux est 1-- et sont également les seuls. cas où la valeur d'entrée est retournée inchangée. Je ne crois pas que ce point important ait été mentionné ailleurs sur cette page.


Le code présenté ci-dessus dépend de la valeur de d utilisée sur le côté droit du xor, qui est la valeur de d mise à jour lors du calcul du côté gauche. Cela semblera évident aux programmeurs C #. Ils sont habitués à voir un code comme celui-ci parce que .NET incorpore formellement un fort { modèle de mémoire } _ qui garantit rigoureusement ici la séquence d'extraction correcte. La raison pour laquelle je mentionne cela est que, dans C ou C++, il peut être nécessaire d'être plus prudent. Les modèles de mémoire de ce dernier sont beaucoup plus permissifs, ce qui peut permettre à certaines optimisations du compilateur d'émettre des extractions dans le désordre. De toute évidence, dans un tel régime, la sensibilité à l'ordre d'extraction représenterait un risque de correction.

0
Glenn Slayden

Quel est le langage de programmation que vous utilisez? En C #, vous pouvez utiliser les méthodes Math.Abs:

int value1 = -1000;
int value2 = 20;
int abs1 = Math.Abs(value1);
int abs2 = Math.Abs(value2);
0
Florin Bombeanu

Pour Assembly, le plus efficace serait d’initialiser une valeur à 0, de soustraire l’entier, puis de prendre le max:

pxor mm1, mm1 ; set mm1 to all zeros
psubw mm1, mm0 ; make each mm1 Word contain the negative of each mm0 Word
pmaxswmm1, mm0 ; mm1 will contain only the positive (larger) values - the absolute value
0
Antonin GAVREL

Si vous n'êtes pas autorisé à utiliser le signe moins, vous pouvez utiliser l'une des méthodes suivantes:

int absVal(int x) {
  return ((x >> 31) + x) ^ (x >> 31);
}
0
Simon Bachmann