将原始的14位二进制补码转换为带符号的16位整数 [英] Convert Raw 14 bit Two's Complement to Signed 16 bit Integer

查看:414
本文介绍了将原始的14位二进制补码转换为带符号的16位整数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用加速度计在嵌入式C中做一些工作,该加速度计以14位2的补码形式返回数据.我将此结果直接存储到uint16_t中.稍后在我的代码中,我试图将这种原始"数据形式转换为一个有符号的整数,以表示/使用我的其余代码.

I am doing some work in embedded C with an accelerometer that returns data as a 14 bit 2's complement number. I am storing this result directly into a uint16_t. Later in my code I am trying to convert this "raw" form of the data into a signed integer to represent / work with in the rest of my code.

我无法让编译器了解我要执行的操作.在下面的代码中,我正在检查是否设置了第14位(表示数字为负),然后我想将这些位取反并加1以得到数字的大小.

I am having trouble getting the compiler to understand what I am trying to do. In the following code I'm checking if the 14th bit is set (meaning the number is negative) and then I want to invert the bits and add 1 to get the magnitude of the number.

int16_t fxls8471qr1_convert_raw_accel_to_mag(uint16_t raw, enum fxls8471qr1_fs_range range) {
  int16_t raw_signed;
  if(raw & _14BIT_SIGN_MASK) {
    // Convert 14 bit 2's complement to 16 bit 2's complement
    raw |= (1 << 15) | (1 << 14); // 2's complement extension
    raw_signed = -(~raw + 1);
  }
  else {
    raw_signed = raw;
  }
  uint16_t divisor;
  if(range == FXLS8471QR1_FS_RANGE_2G) {
    divisor = FS_DIV_2G;
  }
  else if(range == FXLS8471QR1_FS_RANGE_4G) {
    divisor = FS_DIV_4G;
  }
  else {
    divisor = FS_DIV_8G;
  }

  return ((int32_t)raw_signed * RAW_SCALE_FACTOR) / divisor;
}

很遗憾,此代码无法正常工作.反汇编表明,由于某种原因,编译器正在优化我的语句raw_signed = -(~raw + 1);如何实现所需的结果?

This code unfortunately doesn't work. The disassembly shows me that for some reason the compiler is optimizing out my statement raw_signed = -(~raw + 1); How do I acheive the result I desire?

数学运算在纸上完成,但由于某种原因,我觉得编译器正在与我战斗:(.

The math works out on paper, but I feel like for some reason the compiler is fighting with me :(.

推荐答案

将14位2的补码值转换为16位有符号数,同时保持该值仅是:

Converting the 14 bit 2's complement value to 16 bit signed, while maintaining the value is simply a metter of:

int16_t accel = (int16_t)(raw << 2) / 4 ;

左移将符号位推到16位符号位位置,除以四可以恢复幅度,但仍保持其符号.除法避免了实现定义的右移行为,但通常会在允许的指令集上产生单个算术右移.强制转换是必需的,因为raw << 2int表达式,并且除非int是16位,否则除法将简单地恢复原始值.

The left-shift pushes the sign bit into the 16 bit sign bit position, the divide by four restores the magnitude but maintains its sign. The divide avoids the implementation defined behaviour of an right-shift, but will normally result in a single arithmetic-shift-right on instruction sets that allow. The cast is necessary because raw << 2 is an int expression, and unless int is 16 bit, the divide will simply restore the original value.

然而,将加速度计数据只左移两位然后将其视为传感器最初是16位会更简单.将所有内容标准化为16位的好处是,如果您使用的传感器位数不超过16,则代码无需更改.幅度将只是原来的四倍,而最低有效两位将为零-没有信息获得或丢失,并且缩放在任何情况下都是任意的.

It would be simpler however to just shift the accelerometer data left by two bits and treat it as if the sensor was 16 bit in the first place. Normalising everything to 16 bit has the benefit that the code needs no change if you use a sensor with any number of bits up-to 16. The magnitude will simply be four times greater, and the least significant two bits will be zero - no information is gained or lost, and the scaling is arbitrary in any case.

int16_t accel = raw << 2 ;

在两种情况下,如果您想要无符号的幅度,那就很简单:

In both cases, if you want the unsigned magnitude then that is simply:

int32_t mag = (int32_t)labs( (int)accel ) ;

这篇关于将原始的14位二进制补码转换为带符号的16位整数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆