rotacion - ¿Cuándo cambia la conversión de bits de un valor en C++?
tipos de variables en java ejemplos (7)
Tengo un unsigned int
C ++ que en realidad está almacenando un valor firmado. Quiero convertir esta variable a un signed int
, de modo que los valores sin signo y con signo tengan el mismo valor binario.
unsigned int lUnsigned = 0x80000001;
int lSigned1 = (int)lUnsigned; // Does lSigned == 0x80000001?
int lSigned2 = static_cast<int>(lUnsigned); // Does lSigned == 0x80000001?
int lSigned3 = reinterpret_cast<int>(lUnsigned); // Compiler didn''t like this
¿Cuándo cambian los moldes los bits de una variable en C ++? Por ejemplo, sé que la conversión de un int
a un float
cambiará los bits porque int
es dos complementos y float
es un punto flotante. Pero ¿qué pasa con otros escenarios? No tengo claras las reglas para esto en C ++.
En la sección 6.3.1.3 de la especificación C99, se dice que la conversión de un entero sin signo a un entero con signo está definida por el compilador.
El uso de una static_cast
estilo C, o un static_cast
, para emitir un unsigned int
a un signed int
puede permitir que el compilador asigne el primero directamente como si no se hubiera realizado una static_cast
, y por lo tanto puede cambiar los bits si el unsigned int
valor de unsigned int
es mayor que lo que puede contener el signed int
. Sin embargo, un reinterpret_cast
debería funcionar, o puede realizar una conversión de tipos con un puntero:
unsigned int lUnsigned = 0x80000001;
int lSigned1 = *((int*)&lUnsigned);
int lSigned2 = *(reinterpret_cast<int*>(&lUnsigned));
Está buscando int lSigned = reinterpret_cast<int&>(lUnsigned);
No desea reinterpretar el valor de lUnsigned, desea reinterpretar el objeto lUnsigned. Por lo tanto, el elenco a un tipo de referencia.
La conversión es solo una forma de anular el comprobador de tipos, en realidad no debería modificar los bits.
Si convierte desde un tipo de integral con signo más pequeño a un tipo de integral con signo más grande, las copias del bit más significativo original ( 1
en el caso de un número negativo) se anexarán según sea necesario para preservar el valor del entero.
Si lanza un puntero de objeto a un puntero de una de sus superclases, los bits pueden cambiar, especialmente si hay herencia múltiple o superclases virtuales.
Estás preguntando por la diferencia entre static_cast
y reinterpret_cast
.
Una conversión de tipos puede
mantener el valor conceptual (el patrón de bits debe ser cambiado), o
mantenga el patrón de bits (el valor conceptual puede tener que cambiarse).
La única conversión a C ++ que garantiza que siempre se mantiene el patrón de bits es const_cast
.
Un reinterpret_cast
es, como su nombre lo indica, destinado a mantener el patrón de bits y simplemente reinterpretarlo. Pero el estándar permite una implementación mucho margen de maniobra en cómo implementar reinterpret_cast
. En algunos casos, un reinterpret_cast
puede cambiar el patrón de bits.
Un dynamic_cast
generalmente cambia tanto el patrón de bits como el valor, ya que generalmente se adentra en un objeto y devuelve un puntero / referencia a un subobjeto del tipo solicitado.
Un static_cast
puede cambiar el patrón de bits tanto para los enteros como para los punteros, pero casi todas las computadoras existentes utilizan una representación de enteros con signo (llamados complemento de dos) donde static_cast
no cambiará el patrón de bits. Con respecto a los punteros, baste decir que, por ejemplo, cuando una clase base no es polimórfica y una clase derivada es polimórfica, el uso de static_cast
para ir de puntero a derivado a puntero a base, o viceversa, puede cambiar el patrón de bits (como Se puede ver cuando se comparan los punteros void*
). Ahora, enteros ...
Con n bits de valor, un tipo entero sin signo tiene 2 ^ n valores, en el rango de 0 a 2 ^ n -1 (inclusive).
El estándar C ++ garantiza que cualquier resultado del tipo se envuelve en ese rango al sumar o restar un múltiplo adecuado de 2 ^ n .
En realidad así es como lo describe la norma C; el estándar de C ++ solo dice que las operaciones son módulo 2 ^ n , lo que significa lo mismo.
Con el complemento a dos de un valor con signo, x tiene el mismo patrón de bits que el valor sin signo: x + 2 ^ n . Es decir, el mismo patrón de bits que el estándar de C ++ garantiza que se obtiene al convertir - x al tipo sin signo del mismo tamaño. Esos son los conceptos básicos simples de la forma de complemento de dos, que es precisamente la garantía que está buscando. :-)
Y casi todas las computadoras existentes usan la forma de complemento a dos.
Por lo tanto, en la práctica, se le garantiza un patrón de bits sin cambios para sus ejemplos.
unsigned int es siempre del mismo tamaño que int. Y cada computadora en el planeta usa el complemento de 2 en estos días. Así que ninguno de tus lanzamientos cambiará la representación de bits.
Si su implementación utiliza el complemento de 2 para los tipos de enteros con signo, la conversión de tipos de enteros con signo y sin signo del mismo ancho no cambia el patrón de bits.
La conversión de unsigned a signed podría, en teoría, hacer todo tipo de cosas cuando el valor está fuera del rango del tipo firmado, porque está definido por la implementación. Pero lo obvio para la implementación del complemento de 2 es usar el mismo patrón de bits.
Si su implementación no usa el complemento de 2, entonces la conversión entre valores firmados y no firmados cambiará el patrón de bits, cuando el valor firmado involucrado sea negativo. Sin embargo, tales implementaciones son raras (no conozco específicamente ningún uso del complemento no-2 en compiladores de C ++).