2010-11-18 5 views
17

Tengo un C++ unsigned int que en realidad está almacenando un valor firmado. Quiero convertir esta variable en signed int, de modo que los valores sin signo y firmado tengan el mismo valor binario.¿Cuándo cambia el casting los bits de un valor en C++?

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 los moldes cambian 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-complemento y float es de coma flotante. Pero, ¿y otros escenarios? No tengo claro las reglas para esto en C++.

En la sección 6.3.1.3 de la especificación C99 dice que la conversión de un entero sin signo a un entero con signo está definido por el compilador.

+0

De mi propia prueba en VS2008 static_cast (lUnsigned) no cambia los bits. –

+0

Es un detalle de implementación de la representación entera subyacente. Hacer cualquier asunción de 2 cumplidos eventualmente te morderá el culo. –

+0

Buen punto Martin. Sin embargo, tengo una complicación de que sé que mi valor es un número de complemento 2 firmado porque proviene del hardware, y debido a una biblioteca, el valor se almacena en un int sin firmar. ¡Solo quiero devolverlo a un tipo sensato! :) –

Respuesta

14

una conversión de tipos puede

  • mantener el valor conceptual (la bitpattern puede que ser cambiado), o

  • mantener el bitpattern (el valor conceptual puede tienen que estar cambiado).

El único molde C++ que garantiza siempre mantiene la bitpattern es const_cast.

Un reinterpret_cast es, como su nombre indica, la intención de mantener el bitpattern y simplemente reinterpretarlo. Pero el estándar permite una implementación muy amplia en cómo implementar reinterpret_cast. En algunos casos, un reinterpret_cast puede cambiar el patrón de bits.

A dynamic_cast generalmente cambia tanto el patrón de bit como el valor, ya que generalmente profundiza en un objeto y devuelve un puntero/referencia a un subobjeto del tipo solicitado.

Un static_cast puede cambiar el bitpattern tanto para los enteros y los punteros, pero , casi todos los ordenadores existentes usan una representación de enteros con signo (llamado complemento de dos), donde static_cast no va a cambiar la bitpattern. En cuanto a los punteros, basta con decir que, por ejemplo, cuando una clase base no es polimórfico y una clase derivada es polimórfico, utilizando static_cast para ir de puntero a derivado a puntero a la base, o viceversa, puede cambiar la bitpattern (como se puede ver al comparar los punteros void*). Ahora, números 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 C++ estándar garantiza que cualquier resultado del tipo es envuelto en ese rango sumando o restando un múltiplo adecuado de 2^n .

En realidad así es como el estándar de C lo describe; estándar, el C++ sólo dice que las operaciones son modulo 2^n, que significa lo mismo.

Con de dos forma de complemento un valor con signo - x tiene el mismo bitpattern como el valor sin signo - x + 2^n . Es decir, el mismo patrón de bits que el estándar de C++ garantiza que obtiene mediante la conversión - x al tipo sin signo del mismo tamaño. Eso es lo básico simple de la forma de complemento a dos, que es precisamente la garantía lo que estás buscando. :-)

Y casi todos los ordenadores existentes usan forma de complemento a dos.

Por lo tanto, en la práctica, tiene la garantía de un bitpattern sin cambios para sus ejemplos.

+0

Para realizar el reparto sin invocar el comportamiento definido en la implementación, puede usar la respuesta a esto: http://stackoverflow.com/questions/13150449/efficient-unsigned-to-signed-cast-avoiding-implementation-defined-behavior – qbt937

+0

@ qbt937 : ese podría ser un buen ejercicio académico, sí. –

-4

La fundición es solo una forma de anular el verificador de tipos, en realidad no debería modificar los bits.

+0

C/C++ no suele utilizar un bit de signo para valores integrales (se encuentran en números de coma flotante). En cambio, los enteros negativos se representan utilizando la representación de complemento de dos. http://en.wikipedia.org/wiki/Two's_complement –

+2

int c = (int) 1.23 definitivamente modificará los bits. –

+0

@Paul lo siento, no estaba pensando en float -> int. Eso es realmente más coercitivo que el casting, ¿no? –

3

Si transfiere desde un tipo integral firmado más pequeño a un tipo integral firmado más grande, se agregarán copias del bit original más significativo (1 en el caso de un número negativo) 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.

+0

Por lo tanto, refiriéndome únicamente a tipos que no son punteros (valor): ¿qué conversiones harán que los bits cambien, y cuáles no? Parece que static_cast algunas veces cambie los bits, pero reinterpret_cast nunca cambiará los bits –

+0

Uno solo se antepone si el valor original fue negativo. Lo que realmente ocurre es * extensión de signo *; los bits adicionales de orden superior se rellenan con el valor de bit de signo del tipo más pequeño. – Clifford

+0

@emddudley: Ha señalado que los moldes (en realidad, 'static_cast's) de tipos de punto flotante a tipos integrales generalmente cambiarán los bits (excepción:' 0' es todos los bits '0' en ambos tipos). Supongo que el caso de truncamiento (cuenta de bits más grande a más pequeña) también es un caso obvio en el que los bits cambian. No creo que 'reinterpret_cast' garantice que no cambie los bits cuando el tamaño cambie si es válido en ese caso. El problema es que el estándar agita la bandera "dependiente del compilador" en esta pregunta. @Clifford: Gracias. Fijo. –

2

Si su implementación utiliza complemento de 2 para tipos de entero con signo, entonces la conversión de tipos enteros a enteros sin signo del mismo ancho no cambia el patrón de bits.

Casting de unsigned a firmado podría hacer todo tipo de cosas cuando el valor está fuera del rango del tipo con signo, porque está definido por la implementación. Pero lo obvio para la implementación de un complemento de 2 es usar el mismo patrón de bits.

Si su implementación no utiliza el complemento 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 los compiladores de C++).

+0

Buena pena, ¿alguien todavía fabrica computadoras que no son comp de dos? – Crashworks

+0

@Crashworks, si no señala los casos increíblemente raros, entonces alguien seguramente le dará un voto negativo. –

+0

No creo que la conversión de 'signed' a' unsigned', para el mismo conteo de bits, en las máquinas complementarias de uno cambie el patrón de bits tampoco. Tendría que tener un formato donde los enteros * no negativos * se representen de manera diferente ya sea 'signed' o' unsigned'. –

1

Usando una conversión de estilo C, o una static_cast, para emitir una unsigned int a un signed int todavía puede permitir que el compilador para asignar la primera a la segunda directamente como si no se realiza un molde, y por lo tanto puede cambiar los bits si el valor de unsigned int es más grande que el signed int puede contener. Un reinterpret_cast debería funcionar, sin embargo, o puede escribir a presión utilizando un puntero en su lugar:

unsigned int lUnsigned = 0x80000001; 
int lSigned1 = *((int*)&lUnsigned); 
int lSigned2 = *(reinterpret_cast<int*>(&lUnsigned)); 
0

entero sin signo es siempre el mismo tamaño que int. Y cada computadora en el planeta usa el complemento 2 en estos días. Entonces ninguno de tus moldes cambiará la representación de bit.

+0

-1 para afirmación incorrecta "cada computadora ... usa el complemento de 2". E, g, [Unisys Clearpath] (http://en.wikipedia.org/wiki/UNIVAC_1100/2200_series) usa el complemento de 1. –

+0

Pero el modo de complemento de 1 de Unisys Clearpath solo se usa para ejecutar software escrito en las edades oscuras. Lo cual no aplica al OP, quien lo está escribiendo ahora. – TonyK

0

Usted está buscando int lSigned = reinterpret_cast<int&>(lUnsigned);

Usted no quiere reinterpretar el valor de lUnsigned, que desea reinterpretar el objeto lUnsigned. Por lo tanto, el elenco a un tipo de referencia.

+0

No estoy seguro de poder entender esto, pero esto parece muy interesante. ¿Tiene una referencia, o podría ampliar? Gracias. – Benoit

+0

MSalters, eres la primera persona en mencionar esto ... es una idea interesante, voy a experimentar con ella. –

Cuestiones relacionadas