Cuando un int se convierte en corto y truncado, ¿cómo se determina el nuevo valor?

9 minutos de lectura

avatar de usuario
comprardadip

¿Alguien puede aclarar qué sucede cuando un número entero se convierte en un short ¿C ª? Estoy usando Raspberry Pi, así que soy consciente de que un int es de 32 bits, y por lo tanto un short debe ser de 16 bits.

Digamos que uso el siguiente código C, por ejemplo:

int x = 0x1248642;
short sx = (short)x;
int y = sx;

Lo entiendo x se truncaría, pero ¿alguien puede explicar cómo exactamente? ¿Se utilizan turnos? ¿Cómo se trunca exactamente un número de 32 bits a 16 bits?

  • Tenga en cuenta que el yeso (como la mayoría de los yesos) es innecesario. Puedes declarar short sx = x;y el valor de x se convertirá implícitamente en short.

    –Keith Thompson

    19 de enero de 2016 a las 20:12


  • El tamaño real de “int” y “short” puede variar y variará de una plataforma a otra. Pero sí, digamos que “int” es de 32 bits y “short” es de 16 bits: 1) Sí, la conversión truncará el valor de 32 a 16 bits, 2) Sí, los 16 bits superiores se “pierden”, 3) No, no hay “cambio”. PD: ¿Sabía que su Raspberry Pi probablemente tenga una copia completa de Matemática? Definitivamente vale la pena echarle un vistazo 🙂

    – paulsm4

    19 de enero de 2016 a las 20:13


  • No es exactamente un duplicado, pero está estrechamente relacionado: stackoverflow.com/q/19273658/4996248

    – John Coleman

    19 de enero de 2016 a las 20:13

  • Un aparte: puede eliminar las conjeturas de ancho de bits con #include <stdint.h> para traer int32_t, int16_tetc

    – rubicks

    20 de enero de 2016 a las 1:13


avatar de usuario
keith thompson

De acuerdo con el estándar ISO C, cuando convierte un número entero en un tipo con signo y el valor está fuera del rango del tipo de destino, el resultado está definido por la implementación. (O se puede generar una señal definida por la implementación, pero no conozco ningún compilador que haga esto).

En la práctica, el comportamiento más común es que se descarten los bits de orden superior. Entonces suponiendo int es de 32 bits y short es de 16 bits, convirtiendo el valor 0x1248642 probablemente producirá un patrón de bits que parece 0x8642. Y asumiendo una representación de complemento a dos para tipos con signo (que se usa en casi todos los sistemas), el bit de orden superior es el bit de signo, por lo que el valor numérico del resultado será -31166.

int y   =   sx;

Esto también implica una conversión implícita, de short para int. Dado que el rango de int está garantizado para cubrir al menos toda la gama de short, el valor no cambia. (Dado que, en su ejemplo, el valor de sx resulta ser negativo, es probable que este cambio de representación implique extensión de signopropagando la 1 bit de signo a los 16 bits de orden superior del resultado).

Como indiqué, ninguno de estos detalles son requeridos por el lenguaje estándar. Si realmente desea truncar los valores a un tipo más estrecho, probablemente sea mejor usar tipos sin firmar (que tienen un comportamiento envolvente especificado por el idioma) y quizás operaciones de enmascaramiento explícitas, como esta:

unsigned int x = 0x1248642;
unsigned short sx = x & 0xFFFF;

Si tiene una cantidad de 32 bits que desea insertar en una variable de 16 bits, lo primero que debe hacer es decidir cómo desea que se comporte su código si el valor no se ajusta. Una vez que haya decidido eso, puede descubrir cómo escribir código C que haga lo que quiere. A veces, el truncamiento es lo que desea, en cuyo caso su tarea será fácil, especialmente si está utilizando tipos sin firmar. A veces, un valor fuera de rango es un error, en cuyo caso debe verificarlo y decidir cómo manejar el error. A veces, es posible que desee que el valor se sature, en lugar de truncarse, por lo que deberá escribir código para hacerlo.

Es importante saber cómo funcionan las conversiones en C, pero si comienzo con esa pregunta, es posible que esté abordando su problema desde la dirección equivocada.

  • Si su código asume x encajará en un corto, en lugar de enmascarar puede assert( x <= USHRT_MAX ) para hacer cumplir esa suposición.

    – Schwern

    19 de enero de 2016 a las 20:13


  • Aviso x & 0xFFF != (short) x si CHAR_BIT != 8.

    – edmz

    19 de enero de 2016 a las 20:16

  • @black: O, más precisamente, si CHAR_BIT * sizeof (short) != 16. (He trabajado en sistemas con CHAR_BIT==8 donde sizeof (short) es 4 o incluso 8.

    –Keith Thompson

    19 de enero de 2016 a las 20:18

  • A menos que esté programando para una plataforma muy esotérica (y entonces probablemente lo sepa), puede asumir con seguridad que se produce el comportamiento de truncamiento.

    – fuz

    19/01/2016 a las 20:20

  • Respuesta muy útil, en particular los dos últimos párrafos.

    – CompuChip

    20 de enero de 2016 a las 12:59

avatar de usuario
amit

El valor de 32 bits se trunca a 16 bits de la misma manera que se cortaría un pan de plátano de 32 cm de largo si lo metes en un molde de 16 cm de largo. La mitad cabría y seguiría siendo un pan de plátano, y el resto “habrá desaparecido”.

  • No es la mejor analogía. Puedo meter una banana de 32 cm en una fuente de 16 cm aplastándola o cortándola en dos partes una al lado de la otra. Los bits en una palabra tienen restricciones mucho más estrictas que los bits de plátanos en una sartén. Y no dices nada sobre con qué mitad terminas, o por qué.

    –Keith Thompson

    19/01/2016 a las 20:25

  • @KeithThompson: también puede cortar y cortar en dados 32 bits (necesita un cuchillo para el pan o comprar operaciones de manipulación para los bits), pero la analogía requiere empujar el pastel en la sartén, no cortarlo. En cuanto a la parte que entra o la parte que se va, si, no alcancé a incluir ese detalle

    – Amit

    19/01/2016 a las 20:32


  • Voy a tener que decir que esta es la mejor respuesta en SO.

    – Cazador Kohler

    4 de enero a las 2:50

avatar de usuario
edmz

El truncamiento ocurre en los registros de la CPU. Estos tienen diferentes tamaños: 8/16/32/64 bits. Ahora, puedes imaginar un registro como:

<--rax----------------------------------------------------------------> (64-bit)
                                    <--eax----------------------------> (32-bit)
                                                      <--ax-----------> (16-bit)
                                                      <--ah--> <--al--> (8-bit high & low)
01100011 01100001 01110010 01110010 01111001 00100000 01101111 01101110

x primero se le da el valor de 32 bits 0x1248642. En la memoria*, se verá así:

-----------------------------
|  01  |  24  |  86  |  42  |
-----------------------------
 31..24 23..16 15..8  7..0       

Ahora, el compilador carga x en un registro. A partir de él, simplemente puede cargar los 16 bits menos significativos (es decir, ax) y almacenarlos en sx.


*No se tiene en cuenta el carácter endian por simplicidad

  • Creo que el OP quiere saber cómo ocurre ese descarte. ¿Qué 16 bits de los 32 originales se conservan?

    – Schwern

    19 de enero de 2016 a las 20:16

  • @Schwern Gracias, agregué más explicaciones, ¿eso aclara?

    – edmz

    19/01/2016 a las 20:25


  • Si. ¿Serán siempre los 16 bits menos significativos?

    – Schwern

    19/01/2016 a las 20:31

  • @black Envié una edición para corregir un error tipográfico en el resultado, pero no había suficientes caracteres, así que también mejoré (en mi opinión) la ilustración del registro. Siéntase libre de mejorarlo aún más si no está de acuerdo con mi interpretación.

    – Dan Bechard

    20 de enero de 2016 a las 14:42

  • @Dan Gracias Dan, tomé algunas de las sugerencias que propusiste. Debería verse mucho mejor ahora.

    – edmz

    20 de enero de 2016 a las 16:35

avatar de usuario
Zbynek Vyskovsky – kvr000

Simplemente los 16 bits altos se cortan del entero. Por lo tanto, su corto se convertirá en 0x8642 que en realidad es un número negativo -31166.

avatar de usuario
dan bechard

Quizás deje que el código hable por sí mismo:

#include <stdio.h>

#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
#define BYTETOBINARY(byte)  \
   ((byte) & 0x80 ? 1 : 0), \
   ((byte) & 0x40 ? 1 : 0), \
   ((byte) & 0x20 ? 1 : 0), \
   ((byte) & 0x10 ? 1 : 0), \
   ((byte) & 0x08 ? 1 : 0), \
   ((byte) & 0x04 ? 1 : 0), \
   ((byte) & 0x02 ? 1 : 0), \
   ((byte) & 0x01 ? 1 : 0) 

int main()
{
    int x    =   0x1248642;
    short sx = (short) x;
    int y    =   sx;

    printf("%d\n", x);
    printf("%hu\n", sx);
    printf("%d\n", y);

    printf("x: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
        BYTETOBINARY(x>>24), BYTETOBINARY(x>>16), BYTETOBINARY(x>>8), BYTETOBINARY(x));

    printf("sx: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
        BYTETOBINARY(y>>8), BYTETOBINARY(y));

    printf("y: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
        BYTETOBINARY(y>>24), BYTETOBINARY(y>>16), BYTETOBINARY(y>>8), BYTETOBINARY(y));

    return 0;
}

Producción:

19170882
34370
-31166

x: 00000001 00100100 10000110 01000010
sx: 10000110 01000010
y: 11111111 11111111 10000110 01000010

Como puedes ver, int -> short produce los 16 bits inferiores, como se esperaba.

Fundición short para int produce el short con los 16 bits altos establecidos. Sin embargo, sospecho que este es un comportamiento indefinido y específico de la implementación. Básicamente, está interpretando 16 bits de memoria como un número entero, que lee 16 bits adicionales de cualquier basura que haya allí (o 1 si el compilador es bueno y quiere ayudarlo a encontrar errores más rápido).

I pensar debería ser seguro hacer lo siguiente:

int y = 0x0000FFFF & sx;

Obviamente, no recuperará los bits perdidos, pero esto garantizará que los bits altos se pongan a cero correctamente.

Si alguien puede verificar el comportamiento de bit corto -> int alto con una referencia autorizada, se lo agradecería.

Nota: macro binaria adaptada de esta respuesta.

  • Me encantaría saber por qué también se establecen los bits altos, aunque esa es una pregunta aparte

    – comprardadip

    19/01/2016 a las 20:20


  • Esto muestra solo cuál es el comportamiento para la implementación que usó para generar la salida.

    –Keith Thompson

    19/01/2016 a las 20:21

  • @KeithThompson Gracias por la información, Keith. Hice algunas pruebas adicionales y actualicé mi respuesta. Parece que su respuesta es más informada y completa (votada), pero dejaré la mía en caso de que a alguien le apetezca ejecutar el código por curiosidad.

    – Dan Bechard

    19/01/2016 a las 20:41

sx valor será el mismo que 2 bytes menos significativos de xen este caso será 0x8642 que (si se interpreta como un entero con signo de 16 bits) da -31166 en decimal.

  • Me encantaría saber por qué también se establecen los bits altos, aunque esa es una pregunta aparte

    – comprardadip

    19/01/2016 a las 20:20


  • Esto muestra solo cuál es el comportamiento para la implementación que usó para generar la salida.

    –Keith Thompson

    19/01/2016 a las 20:21

  • @KeithThompson Gracias por la información, Keith. Hice algunas pruebas adicionales y actualicé mi respuesta. Parece que su respuesta es más informada y completa (votada), pero dejaré la mía en caso de que a alguien le apetezca ejecutar el código por curiosidad.

    – Dan Bechard

    19/01/2016 a las 20:41

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad