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?
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
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 1
s pour un nombre négatif ou tous les 0
s 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.
Supposons que int
est de 32 bits.
int my_abs(int x)
{
int y = (x >> 31);
return (x ^ y) - y;
}
On peut aussi effectuer l'opération ci-dessus comme:
return n*(((n>0)<<1)-1);
où n
est le nombre dont le nombre absolu doit être calculé.
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)));
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.
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 concerne
0x80000000 (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ématiqueabs(int.MinValue) == int.MinValue
(de même pourlong.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 est1
-- 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.
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);
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
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);
}