C - 添加原始字节时的符号

标签 c bit-shift signed

我的 uint8_t 数组中有传感器数据,这些数据是有符号的(当连接到 16 位值时)。 现在,我想对 N 个周期的值进行平均,这样我就必须将它们添加到一个大于 int16_t 的值,即 int32_t 值。

int32_t val_x = 0 

//...

//do this n times when data is received, dataReceived is a uint8_t array
val_x += ((int16_t)dataReceived[3] << 8) | dataReceived[4];

但我感觉这可能会导致错误。假设我的值为 -1 (0xFFFF) 并将其添加到 32 位有符号值,它会将其解释为正值,对吧?或者编译器是否知道,当我转换为 int16_t 时,这是一个负值,并将在 int32_t 变量 val_x 中将其表示为 0xFFFFFFFF?我是否需要对总值(value)进行更多调整,例如

val_x += (int16_t)(((int16_t)dataReceived[3] << 8) | dataReceived[4]); 

我在 16 位(或可能是带有一些始终为零的位的 24 位)(来自 Microchip 的 PIC24F)上进行编程。 我希望你能明白我遇到的问题。我对这样一个看似微不足道的问题有点迷失......

最佳答案

(int16_t)dataReceived[3] << 8uint8_t dataReceived[3]转换为int16_t 。这种转换是按值(value)进行的;代表它的位是不相关的。既然都是uint8_t值可用 int16_t 表示,结果是 0 到 255 之间的值(含 0 和 255)。在您询问的情况下,该值为 255。

然后对 << 的左操作数执行整数提升 。如果int在此 C 实现中是 16 位,则 int16_tint 的别名或者转换为int 。 (异常(exception):如果 C 实现不使用 int 的补码,则行为会更复杂。但是,本答案不会对此进行讨论。)然后执行移位操作。数学上将 255 左移 8 位得到 65,280。然而,这不能用 16 位 int 来表示。 ,因此行为未定义,根据 C 2018 6.5.7 4。

如果int比 16 位宽,则整数提升提升 int16_tint ,移位不会溢出,结果是 0 到 65,280(含)之间的某个值,即 256 的倍数。

然后输入 dataReceived[4] 进行或运算将其位合并到该值中。结果是 0 到 65,535 之间的值(含 0 和 65,535)。

然后转换为 (int16_t)被申请;被应用。如果值为 0 到 32,767,则为结果。否则,该值无法用 int16_t 表示。 。然后,根据 C 2018 6.3.13,结果是实现定义的或引发实现定义的信号。 GCC 和 Clang 将结果定义为对 216 进行换行,因此这将产生您想要的结果。其他编译器可能不会产生您想要的结果。

如果int是16位,可以通过强制转换 dataReceived[3] 来避免移位溢出至int32_t而不是int16_t 。那么整数提升不会改变它,并且 int32_t值将发生变化。

但是,由于最终转换为 int16_t 时我们仍然需要处理溢出问题,还有另一种方法:

uint16_t tu = (uint16_t) dataReceived[3] << 8 | dataReceived[4];
int16_t  ti;
memcpy(&ti, &tu, sizeof ti);
val_x += ti;

这个:

  • 转化 dataReceived[3]uint16_t所以类次不会溢出。
  • 完成移位和合并并将结果存储在临时 uint16_t 中对象。
  • 复制 uint16_t 的位对象进入 int16_t对象将它们重新解释为 16 位二进制补码整数。
  • 将整数添加到 val_x .

以下代码实际上是相同的,并且避免了命名临时代码,但对于新手 C 程序员来说可能不太熟悉且难以阅读:

val_x += (union { uint16_t u; int16_t i; }) { (uint16_t) dataReceived[3] | dataReceived[4] } .i;

关于C - 添加原始字节时的符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68914843/

相关文章:

C 递增/递减运算符

java - 使用C++获取Java对象调用Java方法

c - 从 fread 中剥离换行符

c++ - BitShifting,将 3 个 u_int8_t 存储在一个整数中并再次读出

java - 将 4 个整数打包成一个 [10,10,10,2] 整数

java - 如何从具有完整权限的本地磁盘加载已签名的小程序?

c++ - 将 uint8_t* 更改为 char*?

c - 向子进程发送数据

java - 使用 >>> 1 如何在将两个数字相加而不是除以 2 时防止溢出?

c - 为什么C语言中每一个signed int类型都必须有一个对应的unsigned int类型?