【问题标题】:C - three bytes into one signed intC - 三个字节转换为一个有符号整数
【发布时间】:2018-04-11 08:42:54
【问题描述】:

我有一个传感器,它以三个字节的形式提供输出。我是这样读的:

unsigned char byte0,byte1,byte2;

byte0=readRegister(0x25);
byte1=readRegister(0x26);
byte2=readRegister(0x27);

现在我想将这三个字节合并为一个数字:

int value;
value=byte0 + (byte1 << 8) + (byte2 << 16);

它给了我从 0 到 16,777,215 的值,但我期望从 -8,388,608 到 8,388,607 的值。我虽然 int 已经被它的实现签名了。即使我尝试像signed int value; 那样定义它,它仍然只给我正数。所以我想我的问题是如何将 int 转换为它的二进制补码?

谢谢!

【问题讨论】:

  • 您不应假定int 是2 的补码或任何其他实现细节。这充其量只会导致不可移植和脆弱的代码。只需自己进行算术运算即可得到正确的结果。事实上,你甚至不应该假设它可以容纳一个 3 字节的数字!
  • 你的架构中的 int 大小是多少?
  • 不要假设大小为int 甚至char。使用正确的类型,如 uint8_tint32_t 等。
  • @underscore_d there is a proposal 在标准的下一个版本中解决这个问题,因为在实践中,非二进制补码算法似乎不是任何代码的问题 IRL 可能是积极更新或新编写。
  • @Stan 即使使用签名类型,也会发生(未)签名扩展,它仅由编译器执行,而不是程序员。很高兴知道它背后的基本逻辑。有时您必须自己知道如何操作:1) 您无法控制输入值的符号性(第 3 方代码)2) 编译器不支持您所需的整数类型(例如 13 位有符号整数或 256 位有符号整数)。

标签: c twos-complement signed-integer


【解决方案1】:

您需要执行的操作称为签名扩展。您有 24 个有效位,但需要 32 个有效位(请注意,您假设 int 是 32 位宽,这并不总是正确的;您最好使用在 stdint.h 中定义的类型 int32_t)。缺少 8 个最高位应该是正值全零或负值全一。它由 24 位值的最高位定义。

int32_t value;
uint8_t extension = byte2 & 0x80 ? 0xff:00; /* checks bit 7 */
value = (int32_t)byte0 | ((int32_t)byte1 << 8) | ((int32_t)byte2 << 16) | ((int32_t)extension << 24);

编辑:请注意,您不能将 8 位值移动 8 位或更多位,这是未定义的行为。您必须先将其转换为更宽的类型。

【讨论】:

  • 整数提升应用于&lt;&lt;的操作数;您根本无法在 C 中移动 8 位值,因为它将首先提升为 intunsigned int。但是,您可能希望防止它选择不合适的转换路径(例如,无符号操作数上的意外符号扩展)。
  • @Leushenko,谢谢,不知道char在移位操作中有特殊待遇。不过,在 C 语言转换时,您永远不会过于小心。
  • @Leushenko:确实,您不能将八位值移动八位而不会冒未定义行为的风险,因为128 &lt;&lt; 8 是32768,它大于int 保证能够代表。
  • @GrigoryRechistov:这不是 char 在移位操作中的特殊处理。在 C 中,所有整数表达式(包括 _Boolcharshort)在大多数情况下都至少提升为 int
【解决方案2】:
#include <stdint.h>
uint8_t byte0,byte1,byte2;
int32_t answer;

// assuming reg 0x25 is the signed MSB of the number 
// but you need to read unsigned for some reason
byte0=readRegister(0x25);
byte1=readRegister(0x26);
byte2=readRegister(0x27);

// so the trick is you need to get the byte to sign extend to 32 bits
// so force it signed then cast it up
answer = (int32_t)((int8_t)byte0); // this should sign extend the number
answer <<= 8;
answer |= (int32_t)byte1; // this should just make 8 bit field, not extended
answer <<= 8;
answer |= (int32_t)byte2;

这也应该有效

answer = (((int32_t)((int8_t)byte0))<<16) + (((int32_t)byte1)<< 8) + byte2;

我可能对括号过于咄咄逼人,但我从不相信自己对移位运算符:)

【讨论】:

    猜你喜欢
    • 2023-04-01
    • 2019-01-05
    • 1970-01-01
    • 2011-12-27
    • 2011-04-21
    • 2018-12-01
    • 2022-01-15
    • 1970-01-01
    • 2014-10-06
    相关资源
    最近更新 更多