通过计算或转换在 C++ 中来回转换 2 的值的补码?

Converting 2's complement of values back and forth in C++ by calculation or casting?

我从硬件寄存器中获取了一些值,这些值存储在 16 位无符号整数中,但这些值实际上是有符号的。知道最后一位是符号位,一位同事做了以下代码片段将它们转换为 2 的补码值:

/* Take 15 bits of the data (last bit is the sign) */
#define DATAMASK 0x7FFF

/* Sign is bit 15 (starting from zero) with the 15 bit data */
#define SIGNMASK 0x8000
#define SIGNBIT  15


int16_t calc2sComplement(uint16_t data)
{
     int16_t temp, sign;
     int16_t signData;

     sign  = (int16_t)((data & SIGNMASK) >> SIGNBIT);

     if (sign) 
     {
          temp = (~data) & DATAMASK;
          signData = (short)(temp * -1);
     }
     else 
     {
          temp = (data & DATAMASK);
          signData = temp;
     }

     return(signData);
}

据我所知,无符号整数类型和有符号整数类型的区别仅在于它们的类型和最后一位的含义;所以像下面这样的铸造也应该有效:

int16_t calc2sComplement(uint16_t data)
{
     return(static_cast<int16_t>(data));
}

并且当需要将值推送到硬件时,与计算不同,反向操作很简单。前一种解决方案的优点是它没有工具链;因为它迟早会改变(gcc 4.4.7,所以 C++03),我宁愿不必这样做,但在多年后编译时不会有任何回归。后者的优点是可读性更好,接近标准,避免不必要的操作。

在我的情况下,如果在工具链更改后再次编译,最好确保保持相同的行为(即使是标准类型也在工具链的某处重新定义,但我真的没有手放在上面)? 如果您保留第一个解决方案,将如何改进它 and/or 编码反向转换(请记住数据可以是数据缓冲区上的指针)?

最后,让我自己回答吧。因此,将值与二进制补码相互转换并防止任何意外行为的最佳方法是执行二进制补码转换,如下所示:

int16_t calc2sComplement(uint16_t data)
{
     return(static_cast<int16_t>(data));
}

并进行反向操作:

uint16_t inv2sComplement(int16_t data)
{
     return(static_cast<uint16_t>(data));
}

这个方法被证明是完全安全的(只要基元类型没有在工具链的某处重新定义——这被认为是不好的做法,但实际上是我的情况,因此我的问题排在第一位)依靠基本内置类型的定义。