Dans cette réponse , zwol a fait cette affirmation:
La bonne façon de convertir deux octets de données d'une source externe en un entier signé 16 bits est avec des fonctions d'assistance comme celle-ci:
#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;
}
Laquelle des fonctions ci-dessus est appropriée dépend du fait que le tableau contient un petit endian ou une grande représentation endian. L'endianité n'est pas le problème en cause ici, je me demande pourquoi zwol soustrait 0x10000u
de la uint32_t
valeur convertie en int32_t
.
Pourquoi est-ce la bonne façon ?
Comment évite-t-il le comportement défini par l'implémentation lors de la conversion au type de retour?
Puisque vous pouvez assumer la représentation du complément à 2, comment cette conversion plus simple échouerait-elle: return (uint16_t)val;
Quel est le problème avec cette solution naïve:
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
ne peut pas être représenté comme int16_t
, et dans la deuxième approche 0xFFFFu
ne peut pas être représenté comme int16_t
.
int16_t
est défini par l'implémentation, de sorte que l'approche naïve n'est pas portable.