c++ - bitwise - operadores de bits en c
Verificar si un nĂºmero es positivo o negativo usando operadores bitwise (15)
¿Puedo verificar si un número es positivo / cero / negativo sin usar ninguna declaración condicional / operadores como if / ternary, etc.
Por supuesto:
bool is_positive = number > 0;
bool is_negative = number < 0;
bool is_zero = number == 0;
Puedo verificar si un número es impar / incluso usando operadores bitwise. ¿Puedo verificar si un número es positivo / cero / negativo sin usar ninguna declaración condicional / operadores como if / ternary, etc.
¿Se puede hacer lo mismo usando operadores bitwise y algún truco en C o en C ++?
Aquí hay una actualización relacionada con C ++ 11 para esta vieja pregunta. También vale la pena considerar signbit() .
En el Compiler Explorer que usa gcc 7.3 64bit con optimización -O3, este código
bool s1(double d)
{
return d < 0.0;
}
genera
s1(double):
pxor xmm1, xmm1
ucomisd xmm1, xmm0
seta al
ret
Y este codigo
bool s2(double d)
{
return std::signbit(d);
}
genera
s2(double):
movmskpd eax, xmm0
and eax, 1
ret
Necesitará crear un perfil para asegurarse de que haya alguna diferencia de velocidad, pero la versión de signbit usa 1 código de operación menos.
Cuando esté seguro sobre el tamaño de un entero (asumiendo 16 bits int):
bool is_negative = (unsigned) signed_int_value >> 15;
Cuando no esté seguro del tamaño de los enteros:
bool is_negative = (unsigned) signed_int_value >> (sizeof(int)*8)-1; //where 8 is bits
La palabra clave unsigned
es opcional.
Es bastante simple
Se puede hacer fácilmente por
return ((!!x) | (x >> 31));
vuelve
- 1 para un número positivo,
- -1 para un negativo, y
- 0 por cero
Esto no se puede hacer de manera portátil con operaciones de bit en C. Las representaciones para tipos de enteros con signo que el estándar permite pueden ser mucho más extrañas de lo que podría sospechar. En particular, el valor con el bit de signo activado y, por lo demás, cero no necesita ser un valor permisible para el tipo firmado ni el tipo sin signo, sino una llamada representación de captura para ambos tipos.
Todos los cálculos con operadores de bit que puede hacer pueden tener un resultado que conduce a un comportamiento indefinido.
En cualquier caso, como sugieren algunas de las otras respuestas, esto no es realmente necesario y la comparación con <
o >
debería ser suficiente en cualquier contexto práctico, es más eficiente, más fácil de leer ... así que hazlo de esa manera.
Hay una discusión detallada en la página de Bit Twiddling Hacks .
int v; // we want to find the sign of v
int sign; // the result goes here
// CHAR_BIT is the number of bits per byte (normally 8).
sign = -(v < 0); // if v < 0 then -1, else 0.
// or, to avoid branching on CPUs with flag registers (IA32):
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
// or, for one less instruction (but not portable):
sign = v >> (sizeof(int) * CHAR_BIT - 1);
// The last expression above evaluates to sign = v >> 31 for 32-bit integers.
// This is one operation faster than the obvious way, sign = -(v < 0). This
// trick works because when signed integers are shifted right, the value of the
// far left bit is copied to the other bits. The far left bit is 1 when the value
// is negative and 0 otherwise; all 1 bits gives -1. Unfortunately, this behavior
// is architecture-specific.
// Alternatively, if you prefer the result be either -1 or +1, then use:
sign = +1 | (v >> (sizeof(int) * CHAR_BIT - 1)); // if v < 0 then -1, else +1
// On the other hand, if you prefer the result be either -1, 0, or +1, then use:
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
// Or, for more speed but less portability:
sign = (v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1)); // -1, 0, or +1
// Or, for portability, brevity, and (perhaps) speed:
sign = (v > 0) - (v < 0); // -1, 0, or +1
// If instead you want to know if something is non-negative, resulting in +1
// or else 0, then use:
sign = 1 ^ ((unsigned int)v >> (sizeof(int) * CHAR_BIT - 1)); // if v < 0 then 0, else 1
// Caveat: On March 7, 2003, Angus Duggan pointed out that the 1989 ANSI C
// specification leaves the result of signed right-shift implementation-defined,
// so on some systems this hack might not work. For greater portability, Toby
// Speight suggested on September 28, 2005 that CHAR_BIT be used here and
// throughout rather than assuming bytes were 8 bits long. Angus recommended
// the more portable versions above, involving casting on March 4, 2006.
// Rohit Garg suggested the version for non-negative integers on September 12, 2009.
Los enteros con signo y los puntos flotantes normalmente utilizan el bit más significativo para almacenar el signo, por lo que si conoce el tamaño, puede extraer la información del bit más significativo.
Por lo general, hay poco beneficio al hacer esto, ya que se necesitará hacer algún tipo de comparación para usar esta información y es tan fácil para un procesador probar si algo es negativo como si no es cero. Si es un hecho en los procesadores ARM, verificar el bit más significativo normalmente será MÁS costoso que comprobar si es negativo desde el principio.
Puede diferenciar entre negativo / no negativo observando el bit más significativo. En todas las representaciones para enteros con signo, ese bit se establecerá en 1 si el número es negativo.
No hay ninguna prueba para diferenciar entre cero y positivo, a excepción de una prueba directa contra 0.
Para probar el negativo, puedes usar
#define IS_NEGATIVE(x) ((x) & (1U << ((sizeof(x)*CHAR_BIT)-1)))
Si el bit alto se establece en un entero con signo (byte, largo, etc., pero no un número de coma flotante), ese número es negativo.
int x = -2300; // assuming a 32-bit int
if ((x & 0x80000000) != 0)
{
// number is negative
}
ADICIONAL:
Usted dijo que no quiere usar ningún condicional. Supongo que podrías hacer esto:
int isNegative = (x & 0x80000000);
Y en algún momento posterior puede probarlo con if (isNegative)
.
Supongamos que su número es a=10
(positivo). Si cambias a
a
te dará cero.
es decir:
10>>10 == 0
Entonces puedes verificar si el número es positivo, pero en el caso de a=-10
(negativo):
-10>>-10 == -1
Así que puedes combinarlos en un if
:
if(!(a>>a))
print number is positive
else
print no. is negative
Una forma más sencilla de averiguar si un número es positivo o negativo: deje que el número sea x verifique si [x * (-1)]> x. Si es cierto, x es negativo o positivo.
#include<stdio.h>
void main()
{
int n; // assuming int to be 32 bit long
//shift it right 31 times so that MSB comes to LSB''s position
//and then and it with 0x1
if ((n>>31) & 0x1 == 1) {
printf("negative number/n");
} else {
printf("positive number/n");
}
getch();
}
// if (x < 0) return -1
// else if (x == 0) return 0
// else return 1
int sign(int x) {
// x_is_not_zero = 0 if x is 0 else x_is_not_zero = 1
int x_is_not_zero = (( x | (~x + 1)) >> 31) & 0x1;
return (x & 0x01 << 31) >> 31 | x_is_not_zero; // for minux x, don''t care the last operand
}
Esto es exactamente lo que querías!
if( (num>>sizeof(int)*8 - 1) == 0 )
// number is positive
else
// number is negative
Si el valor es 0, el número es positivo o negativo