En esta respuesta , zwol hizo esta afirmación:
La forma correcta de convertir dos bytes de datos de una fuente externa en un entero con signo de 16 bits es con funciones auxiliares como esta:
#include <stdint.h>
int16_t be16_to_cpu_signed(const uint8_t data[static 2]) {
uint32_t val = (((uint32_t)data[0]) << 8) |
(((uint32_t)data[1]) << 0);
return ((int32_t) val) - 0x10000u;
}
int16_t le16_to_cpu_signed(const uint8_t data[static 2]) {
uint32_t val = (((uint32_t)data[0]) << 0) |
(((uint32_t)data[1]) << 8);
return ((int32_t) val) - 0x10000u;
}
Cuál de las funciones anteriores es apropiada depende de si la matriz contiene una representación little endian o big endian. Endianness no es el problema en cuestión aquí, me pregunto por qué zwol resta 0x10000u
del uint32_t
valor convertido a int32_t
.
¿Por qué es esta la forma correcta ?
¿Cómo evita el comportamiento definido de implementación cuando se convierte al tipo de retorno?
Como puede asumir la representación del complemento de 2, ¿cómo podría fallar este elenco más simple? return (uint16_t)val;
Lo que está mal con esta solución ingenua:
int16_t le16_to_cpu_signed(const uint8_t data[static 2]) {
return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
}
int16_t
0xFFFF0001u
no se puede representar como int16_t
, y en el segundo enfoque 0xFFFFu
no se puede representar como int16_t
.
int16_t
está definido por la implementación, por lo que el enfoque ingenuo no es portátil.