¿Como funciona esto?
La idea es hacer que abs(x) use operadores bit a bit para números enteros (suponiendo palabras de 32 bits):
y = x >> 31 (x + y) ^ y // This gives abs(x) (is ^ XOR)?Este enfoque se basa en muchos comportamientos específicos de implementación:
x tiene 32 bits de ancho. Sin embargo, podría arreglar esto con x >> (sizeof(x) * CHAR_BIT - 1)Ejemplo con 3 bits:
101 -> x = -3 111 -> x >> 2 101 + 111 = 100 -> x + y 100 XOR 111 -> 011 -> 3Esto no es portátil.
Suponiendo palabras de 32 bits, como se indica en la pregunta:
Para x negativa, x >> 31 está definida por la implementación en los estándares C y C++. El autor del código espera enteros en complemento a dos y un desplazamiento aritmético a la derecha, en el que x >> 31 produce todos los bits cero si el bit de signo de x es cero y todos los bits uno si el bit de signo es uno.
Por lo tanto, si x es positivo o cero, y es cero y x + y es x , entonces (x + y) ^ y es x , que es el valor absoluto de x .
Si x es negativo, y es todo unos, lo que representa −1 en complemento a dos. Entonces x + y es x - 1 . Entonces XORing con todos unos invierte todos los bits. Invertir todos los bits es equivalente a tomar el complemento a dos y restar uno, y el complemento a dos es el método utilizado para negar números enteros en formato de complemento a dos. En otras palabras, XORing q con todos unos da -q - 1 . Entonces x - 1 XORed con todos unos produce -(x - 1) - 1 = -x + 1 - 1 = -x , que es el valor absoluto de x excepto cuando x es el valor mínimo posible para el formato (−2,147,483,648 para complemento a dos de 32 bits), en cuyo caso el valor absoluto (2,147,483,648) es demasiado grande para representarlo, y el patrón de bits resultante es solo el x original.
Uso este código, primero el cálculo del complemento a dos (el guardia solo asegura con una verificación de tiempo de compilación, la plantilla es un número entero)
/** * Zweierkomplement - Two's Complement */ template<typename T> constexpr auto ZQ(T const& _x) noexcept ->T{ Compile::Guards::IsInteger<T>(); return ((~(_x))+1); }y en un segundo paso esto se usa para calcular el entero abs()
/** * if number is negative, get the same number with positiv sign */ template<typename T> auto INTABS(T const _x) -> typename std::make_unsigned<T>::type{ Compile::Guards::IsInteger<T>(); return static_cast<typename std::make_unsigned<T>::type>((_x<0)?(ZQ<T>(_x)):(_x)); } por qué uso este tipo de código:
* comprobaciones en tiempo de compilación
* funciona con todos los tamaños de enteros
* portátil desde pequeños µC hasta núcleos modernos
* Está claro que debemos considerar el complemento a dos, por lo que necesita un valor de retorno sin signo, por ejemplo, para 8 bits abs (-128) = 128 no se puede expresar en un entero con signo