【问题标题】:How to convert 2 bytes into a signed short in C如何在 C 中将 2 个字节转换为有符号短
【发布时间】:2019-06-22 14:51:55
【问题描述】:

我有 2 个字节需要转换为有符号短数字。例如,我在二进制中有单独的字节 (0000 0001) 和 (0000 0002)。如何将这些转换为带符号的短值?

【问题讨论】:

  • 你想用0000 0001和0000 0002这两个数字达到什么效果?
  • byte1<<8 | byte2
  • (0000 0002) 是一个字节值吗?
  • 鉴于您应用的标签,您至少知道答案涉及什么 - 在这种情况下,向我们展示您的尝试并解释它是如何不起作用的。您不知道答案但知道它需要位/字节移位的事实表明这是一个家庭作业问题。我暂时删除了我的答案-这不是作业作弊网站。该行业不再需要无法真正为自己编码的毕业生。此外,它几乎可以肯定是重复的。
  • @clifford:这实际上不是一个很好的家庭作业问题,因为幼稚的解决方案是不正确的;它涉及将超出范围的值(通常是隐式地)转换为有符号的short。 “教授 C 编程”实际上传递了坏习惯和误解,这有点令人沮丧。我怀疑我的答案,虽然我坚持认为它是正确的,但这并不是教授在这种情况下所要寻找的。​​span>

标签: c bit byte-shifting


【解决方案1】:

如果字节保存在带符号的数据类型中,例如 signed charint8_t,那么它非常简单:

signed short combine_signed(signed char byte1, signed char byte2) {
  return byte1 * 256 + (uint8_t)byte2;
}

这里使用的是乘法,而不是移位运算,但预计编译器实际上会插入适当的移位运算。 C 标准没有规定左移负数的结果,因此在可移植代码中不能使用左移。

如果字节是无符号类型或宽于 8 位的类型,那么最简单的方法是先将高位字节转换为有符号值,然后再如上处理。转换为有符号值不能通过简单的强制转换来完成,因为这样的转换将是整数溢出,其结果未由 C 标准指定。所以一个可移植的程序必须显式地测试高位:

signed short combine(int byte1, int byte2) {
  // This code assumes that byte1 is in range, but allows for the possibility
  // that the values were originally in a signed char and so is now negative.
  if (byte1 >= 128) byte1 -= 256;
  return byte1 * 256 + (uint8_t)byte2;
}

(用于 x86 的 gcc 和 clang,使用 -O2 或更高版本编译,设法将其简化为简单的三指令序列,无需乘法或条件。)

【讨论】:

  • 一个常见的场景是一个 8 位微控制器,一个 12 位 ADC 带有“左对齐”(即 LSB 的最低有效四位始终为零),这样您就可以读取两个8 位值并将它们解释为 16 位二进制补码值。在这种情况下,您会将“字节”读取为两个uint8_t,因此引用的规则将不适用,并且隐式提升是安全的。答案是正确的,打字不太可能 - 问题缺少可能答案的必要信息。
  • @clifford:我为两种打字场景提供了答案。 (uint8_t 在语义上与int 相同,因为算术提升。)当然其中之一是可能的,不是吗?使用uint8_t,移位是安全的,但不是(隐式)强制转换为有符号返回值。
  • @rici :也许不是嵌入式系统工程师会这样写 - 我很欣赏它没有被标记为“嵌入式”,但它有那种味道。我正在取消删除我的答案以表明我对此的看法。它涵盖了您提出的所有问题,但我建议以一种更惯用的方式 - 邀请 cmets!
【解决方案2】:

给定:

char msb = 0x01 ;
char lsb = 0x02 ;

然后:

short word = (msb << 8) | (lsb & 0xff) ;

将导致 word 的值为 0x0102(或 25810)。

由于您要求签署短片,但这不是一个非常有趣的例子。对于:

char msb = 0x80 ;
char lsb = 0x02 ;

word 将具有 0x8002,对于 16 位 short 将是 -32766。

但是,在short 长度超过 16 位(允许)的实现中,结果将被解释为 +32770。在这种情况下,使用在stdint.h 中定义的固定大小的int16_t 类型来避免任何潜在的实现依赖性要安全得多。

 int16_t word = (msb << 8) | (lsb & 0xff) ;

这可以通过使用uint8_t而不是char来稍微简化,char可以是有符号的也可以是无符号的:

uint8_t msb = 0x80u ;
uint8_t lsb = 0xFFu ;
int16_t word = (msb << 8) | lsb ;

将导致word = -32513,而如果lsbmsbcharchar 在实现中被签名,那么由于隐式类型提升和签名,结果将是-1 lsb 的扩展名。

这仍然没有严格定义,因为左侧表达式提升为 unsigned int 并且可能导致无法表示为 int16_t 的值,在这种情况下,行为是实现定义的。也就是说,这将是一个不寻常的实现,除了简单地逐字复制这些位之外,它还做了其他任何事情,这就是它起作用的原因,而且上面是惯用的。

如果明确需要short,为了保证正确签名的结果而不管short 的长度,您可以明确转换为int16_t 并分配给short(甚至是int):

 short word = (int16_t)((msb << 8) | (lsb & 0xFF));

也可以使用联合解决方案,但鉴于此问题的标签,在这种情况下它似乎不太可能是可接受的解决方案。它的优点是避免了任何实现定义的行为和神秘的类型提升和隐式转换规则,但你必须处理字节序:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  #define LSB 0
  #define MSB 1
#else
  #define LSB 1
  #define MSB 0
#endif

union
{
    int16_t word ;
    uint8_t byte[2] ;
} reinterpret ; 

reinterpret.byte[MSB] = 0x80u ;
reinterpret.byte[LSB] = 0xFFu ;

short word = reinterpret.word ;

https://onlinegdb.com/Byth1N3yr

【讨论】:

  • 我很欣赏,在大多数实现中,这将完美无缺,而且它可能是惯用的。但我坚持:int16_t word = (msb &lt;&lt; 8) | lsb ; 其中msb 包含大于等于 0x80 的值是未指定的,因为右侧的值不能表示为 int16_t,导致 6.3.1.3/3:“否则,新的类型是有符号的,并且值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。”如果您的实现定义了结果,那很好。例如,GCC 就是这样做的。但它并不完全便携。
  • @rici:是的。将添加一些警告。
  • 还可以对char 进行签名... ymmv 与乘法!
  • 在 C99+ 中,可以在单个表达式中进行联合重新解释:(union reinterpret){.byte = {[LSB] = 0x80u, [MSB] = 0xFFu}}.word;
  • @AnttiHaapala 在这种情况下, reinterpret 是一个变量名而不是联合标签,但我明白你的意思。它不是(还)有效的 C++,这是我通常使用的,所以我不会想出那个。
【解决方案3】:

假设 0x01 是 MSB,0x02 是 LSB,那么 unsigned short foo = 0x01 &lt;&lt; 8 | 0x02; 就足够了。但是,这意味着unsigned short 至少为 16 位(取决于实现,搜索 stdint.h 以获得固定大小)

【讨论】:

  • "如何将这些转换为 signed 短值?"
  • 1) 问题要求签名短,而不是未签名短。 2) 语言保证 short至少 16 位,因此它不依赖于实现。对于已签名,如果short 的长度超过 16 位,这将是一个问题。
  • @Clifford 有符号和无符号之间的区别无关紧要。唯一重要的是你如何表现它。
  • @Nina :如果问题指定了,那它怎么不相关?此外,它在任何情况下都是完全相关的。如果你有 msb=0xFFlsb = 0xFF 并且做了 unsigned short x = msb &lt;&lt; 8 | lsb ; 然后将 x 分配给 int32_t y = x ; 然后 y 将包含 65535 而不是 -1,因为如果 x 是有符号的短,它将包含。认为类型协议无关紧要会导致代码中出现一些有趣的错误。
  • @Clifford 我明白了。
猜你喜欢
  • 2010-09-22
  • 2016-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
相关资源
最近更新 更多