C -添加原始字节时的签名



我在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?我是否需要对

这样的值进行更多的强制转换?
val_x += (int16_t)(((int16_t)dataReceived[3] << 8) | dataReceived[4]); 

我在16位(或可能是24位与一些始终为零的位)(PIC24F从Microchip)上编程。我希望你能理解我的问题。这样一个看似微不足道的问题把我弄糊涂了。

(int16_t)dataReceived[3] << 8中,uint8_tdataReceived[3]转化为int16_t。该转换按值操作;表示它的位是不相关的。因为所有uint8_t的值都可以在int16_t中表示,所以结果是一个从0到255的值,包括0到255。在您询问about的情况下,值是255。

则对<<的左操作数进行整数提升。如果int在这个C实现中是16位,那么int16_t要么是int的别名,要么被转换为int。(例外:如果C实现不使用int的二补,则行为更复杂。然而,这个答案不会涉及到这一点。)然后执行移位操作。将255左移8位数学上得到65280。然而,这在16位int中是不可表示的,因此行为是未定义的,参见C 2018 6.5.7 4。

如果int大于16位,则整型提升将int16_t提升到int,移位不会溢出,结果是0到65535(包括65535)之间的某个值,即256的倍数。

那么在dataReceived[4]中的ORing将其位合并到该值中。取值范围为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;

不要害怕使用内联函数来使代码更清晰:

#ifdef __GNUC__
#define ALWAYS_INLINE __attribute__((always_inline))
#else
#define ALWAYS_INLINE
#endif
static inline ALWAYS_INLINE int16_t combineInt16(uint8_t *d)
{
int16_t i16;
memcpy(&i16, d, sizeof(i16));
return i16;
}
int16_t average(uint8_t *data, size_t size)
{
int32_t average = 0;
for(size_t index = 0; index < size / 2; index ++)
{
average += combineInt16(data);
data += 2;
}
return average / size;
}

https://godbolt.org/z/GcEKoGeWM

相关内容

  • 没有找到相关文章

最新更新