2バイトのデータを外部ソースから16ビットの符号付き整数に変換する正しい方法は、次のようなヘルパー関数を使用することです。
#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;
}
上記の関数のどちらが適切かは、配列にリトルエンディアン表現またはビッグエンディアン表現が含まれるかどうかによって異なります。エンディアンネスはここでの問題ではありません、なぜzwolがに変換さ0x10000u
れたuint32_t
値から減算するのか疑問に思っていint32_t
ます。
なぜこれが正しい方法ですか?
戻り値の型に変換するときに、実装で定義された動作をどのように回避しますか?
2の補数表現を想定できるので、この単純なキャストはどのように失敗しますか。 return (uint16_t)val;
この素朴なソリューションの何が問題になっていますか:
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
はint16_t
、0xFFFFu
として表現できません。2番目のアプローチでは、として表現できませんint16_t
。
int16_t
は実装によって定義されるため、単純なアプローチは移植できません。