2009-11-17 13 views
20

tengo un valor como esto:interpretan como no firmada firmado

int64_t s_val = SOME_SIGNED_VALUE; 

¿Cómo puedo obtener una

uint64_t u_val 

que tiene exactamente el mismo patrón de bits como s_val, pero es tratado como sin firmar?

Esto puede ser realmente simple, pero después de buscar en Stackoverflow y en otros lugares no encontré la respuesta.

+0

Votos para todos; Gracias por tu contribución. Creo que static_cast es, de hecho, la respuesta correcta. Para que conste, lo había intentado primero, pero debido a un error grave en otra parte, pensé que no estaba preservando el patrón de bits. Para aclarar la pregunta, está bien que s_val! = U_val (que será el caso si s_val <0). Los bits son lo que importa. – bbg

Respuesta

28
int64_t s_val = SOME_SIGNED_VALUE; 
uint64_t u_val = static_cast<uint64_t>(s_val); 

C++ estándar de 4,7/2 establece que:

Si el tipo de destino es sin signo, el valor resultante es el entero sin signo menos congruente con el número entero fuente (módulo 2 n donde n es la cantidad de bits utilizados para representar el tipo sin firmar). [Nota: en una representación de complemento de dos, esta conversión es conceptual y no hay cambios en el patrón de bits (si no hay truncamiento). ]

De otra parte, Standard dice que "El mapeo realizado por reinterpret_cast es definido por la implementación [Nota:.. Que podría, o no puede, producir una representación diferente del valor original]" (5.2. 10/3). Por lo tanto, recomendaría usar static_cast.

2

También puede reinterpret_cast, o utilizar un union:

union { 
    int64_t i64; 
    uint64_t ui64; 
} variable; 

variable.i64 = SOME_SIGNED_VALUE; 
uint64_t a_copy = variable.ui64; 
+1

'static_cast' no conducirá a perder el patrón de bits. –

+0

no es así. Me pregunto por qué no ... – xtofl

+1

Estaba un poco preocupado por static_cast <>. También iba a sugerir el uso de reinterpret_cast <> debido a la declaración de patrón de bits. ¿Estás seguro de que static_cast <> funcionará (creo que no tendrá una copia del estándar a mano). Pero reinterpret_cast <> es también una indicación de que es un lanzamiento inseguro. –

6

En términos generales, no importa si se utiliza o static_cast<int64_t>reinterpret_cast<int64_t>. Siempre que se ejecute en un procesador que usa two's complement para representar números negativos, el resultado es el mismo. (Prácticamente todos los procesadores modernos usan eso.) En el complemento a dos, un número positivo en un int firmado se representa de la misma manera en un int sin signo; si es un número negativo, se lo reinterpretará como un número positivo grande en la forma sin firmar.

Básicamente, lo que su elenco hace es decirle al compilador que produzca instrucciones de ensamblaje diferentes cuando se trata de ese valor. P.ej. hay diferentes instrucciones para la multiplicación y división de enteros con signo. Aunque la suma y la resta siguen siendo las mismas (lea el enlace de wikipedia y lo comprenderá).

+0

Pero ¿qué pasa con un valor negativo? –

+0

La regla es "si el nuevo tipo no está firmado, el valor se convierte al agregar o restar repetidamente uno más que el valor máximo que se puede representar en el nuevo tipo hasta que el valor esté en el rango del nuevo tipo", que funciona para el complemento de 2, y creo que el complemento de 1, pero no juraría que mantuvo el mismo patrón de bits para más representaciones bizzaro de números negativos. –

+0

Lo anterior es del estándar C99, el estándar de C++ dice esto: "el valor resultante es el entero menos sin signo congruente con el entero de origen (módulo 2n donde n es el número de bits utilizados para representar el tipo sin signo). [Nota: En una representación de complemento de dos, esta conversión es conceptual y no hay ningún cambio en el patrón de bits (si no hay truncamiento).] " –

5

Patrón de bits lógicos (bits de representación de valores), es decir, los valores de dígitos binarios solo se pueden conservar si el valor firmado original no fue negativo, porque los valores negativos no se pueden representar con una variable entera sin signo. Todo lo que necesita hacer es asignar su valor con signo a su objeto entero sin signo y ya está

uint64_t u_val = s_val; 

una conversión explícita no es necesario, pero podría usarse para suprimir las advertencias del compilador.

En cuanto al patrón de bits físicos (es decir, lo que ve en la memoria sin procesar, bits de representación de objetos), simplemente no puede "convertirlo" de esa manera. El lenguaje C++ no le proporciona ningún método de conversión que le garantice preservar el patrón de bits físicos.Todo lo que puede hacer es reinterpretar la memoria ocupada por el objeto firmado como un objeto sin firma del mismo tamaño

STATIC_ASSERT(sizeof(int64_t) == sizeof(uint64_t)); 
uint64_t u_val = reinterpret_cast<uint64_t&>(s_val); 

Una vez más, esto no es una conversión, sino más bien una reinterpretación de la memoria. Esto no está garantizado para funcionar y esto es generalmente ilegal.

+0

¡Buen punto para distinguir "conversión" de "interpretación"! – xtofl

+0

¿Cómo es ilegal? ¿Qué quieres decir con 'trabajo'? Puedes "reinterpretar" de nuevo, ¿no? – xtofl

+0

Es ilegal porque el lenguaje C++ prohíbe acceder a la memoria ocupada por un objeto de tipo 'T' como un objeto de tipo diferente' U' (con algunas excepciones). En otras palabras, leer la memoria reinterpretada es casi siempre ilegal. – AnT

10

Tenga en cuenta que no necesita el molde en absoluto. A pesar de todas las disputas acerca de si el elenco cometerá bits o no de las representaciones negativas, una cosa se ha perdido: el reparto es completamente innecesario.

Debido a las conversiones que C/C++ hará (y cómo se define la fundición), esto:

int64_t s_val = SOME_SIGNED_VALUE; 
uint64_t u_val = s_val; 

es exactamente equivalente a:

int64_t s_val = SOME_SIGNED_VALUE; 
uint64_t u_val = static_cast<uint64_t>(s_val); 

Dicho esto, es posible que aún desee el elenco porque señala la intención. Sin embargo, he escuchado que argumentó que no debe usar moldes innecesarios porque puede silenciar el compilador en situaciones donde puede querer una advertencia.

Elija su veneno.

+1

Ajá, ese es un buen punto. En mi caso, creo que es mejor señalar la intención, pero me alegra ver la advertencia. – bbg

5

Acepto que static_cast es apropiado en este caso, pero nadie ha mencionado un caso de aspecto muy similar donde static_cast no conservará los bits como podría esperarse.

char x = -1; // 255 
unsigned int x2 = static_cast<unsigned int>(x); // 4294967295 
unsigned int x3 = static_cast<unsigned int>(static_cast<unsigned char>(x)); // 255 

Cuidado con sign extension cuando se está lanzando desde un valor pequeño firmado a un gran valor sin signo. Posiblemente otras combinaciones también sean vulnerables; no lo he pensado todo el tiempo.

+0

Punto interesante; gracias por explicar y proporcionar el enlace para firmar la extensión. – bbg

1

Quería compartir esta solución moderna y genérica de C++ 14. Originalmente demostrado here.

template<class T> 
auto as_unsigned(T t) 
{ 
    return std::make_unsigned_t<T>(t); 
} 

que pueden ser usados ​​de la siguiente manera:

auto sx = int32_t{ 55 }; 
auto ux = as_unsigned(sx); 

Usted puede verlo en acción here.

Cuestiones relacionadas