从2';嵌入C的s补码值

从2';嵌入C的s补码值,c,embedded,C,Embedded,我知道这里有很多关于转换二的补码格式的类似问题,我尝试了很多,但在我的案例中似乎没有任何帮助 嗯,我正在做一个嵌入式项目,涉及通过SPI写入/读取从设备的寄存器。这里涉及的寄存器是一个22位位置寄存器,它以2的补码格式存储uStep值,其范围为-2^21到+2^21-1。问题是当我读取寄存器时,我得到一个与实际值无关的大整数 例如: 在向从属设备发送移动4000步(正向/正向)的命令后,我读取了位置寄存器,得到了精确的4000步。但是,如果我发送一个反向移动命令,比如-1,然后读取寄存器,我得到

我知道这里有很多关于转换二的补码格式的类似问题,我尝试了很多,但在我的案例中似乎没有任何帮助

嗯,我正在做一个嵌入式项目,涉及通过SPI写入/读取从设备的寄存器。这里涉及的寄存器是一个22位位置寄存器,它以2的补码格式存储uStep值,其范围为-2^21到+2^21-1。问题是当我读取寄存器时,我得到一个与实际值无关的大整数

例如:

在向从属设备发送移动4000步(正向/正向)的命令后,我读取了位置寄存器,得到了精确的4000步。但是,如果我发送一个反向移动命令,比如-1,然后读取寄存器,我得到的值类似于42928。我相信这是寄存器的负偏移量,因为二者的补码没有零。我可以向设备发送一个负整数来移动x步数,但是,从检索到的值中获取实际的负整数是另一回事


我知道这涉及到2的补码,但问题是,如何从这个奇怪的值中得到实际的负整数?我的意思是,如果我移动设备-4000步,我必须做什么才能得到从我的寄存器移动到如此远的负步的确切值

您需要对扩展位21进行签名,使其穿过左侧的位

对于设置位21时的负值,可以通过使用
0xFFC00000
修改该值来实现


对于清除位21时的正值,您可以通过使用
0x003FFFFF

对值进行ANDing来确保,最简单的方法可能只是将位置值左移10位,然后分配给
int32\t
。然后,您将有一个32位的值,位置将按210(1024)的比例放大,并且具有32位的分辨率,但粒度为10位,这通常不重要,因为位置单位在任何情况下都是完全任意的,并且可以在必要时转换为真实世界的单位,考虑到缩放:

int32_t pos_count = (int32_t)(getPosRegisterValue() << 10) ;
这两种解决方案都依赖于实现定义的行为,即铸造一个
uint32_t
中无法表示的值;但一对二的补码机任何看似合理的实现都不会修改位模式,结果将符合要求

另一个可能不太优雅的解决方案(也保留22位分辨率和单位粒度)是:

int32_t pos_count = getPosRegisterValue() ;

// If 22 bit sign bit set...
if( (pos_count & 0x00200000) != 0)
{
    // Sign-extend to 32bit
    pos_count |= 0xFFC00000 ;
}
将解决方案包装为隔离任何实现定义的行为的功能可能是明智的:

int32_t posCount()
{
    return (int32_t)(getPosRegisterValue() << 10)) / 1024 ;
}
int32\u t posCount()
{

返回(int32_t)(getPosRegisterValue()Clifford和Weather Vane的解决方案假设目标机器是二元补码。这很可能是真的,但消除这种依赖性的解决方案是:

static const int32_t sign_bit = 0x00200000;

int32_t pos_count = (getPosRegisterValue() ^ sign_bit) - sign_bit;

它还具有无分支的额外优势。

转换为二者的补码符号位也是未定义的行为(C99§6.5.7/3-4)。请参阅我的答案以获取替代方案。当然,这是一个2的补码机器,因为这已在设备的数据表中明确提到。所有答案都很好,只是尝试了其中的两个,他们解决了我的问题。我不能再期待了!请注意,我已经删除了关于右移的部分;所有解决方案都显示出未定义的行为正如@DougCurrie所指出的,这可以通过他的解决方案或者通过
getPosRegisterValue()来解决
returning
uint32\u t
-使用MSB集强制转换无符号值是实现定义的,而不是未定义的,因此对于任何特定的编译器/目标组合,如果不可移植,它至少会被定义。我更改了答案以反映这一点。是的,这实际上是我用来解决它的方法,避免了其他解决方案不要涉及转换。这只适用于2的补码机器,这是公认的标准。请参阅我的答案以获得替代方案。不牺牲效率的良好目标不可知解决方案。尽管在27年的嵌入式开发中,我从未遇到过不是2的补码的目标。27年来,我也从未想过这一点解决方案,就是这样!我想这也假设目标是二位补码。这不是存在
int32\t
类型的要求吗?int32\t是一个有符号的32位整数,格式没有指定。我用它来表示数字大于22位,但代码可以处理任何大于22位的有符号整数类型位。这还取决于设备/协议的Endianness与CPU的Endianness。您考虑过这一部分吗?无论如何,请发布实际的有问题的代码,因为如果没有示例,这将很难回答。嗯,不是真的,但我相信答案假定是一台大端机,它们工作正常,没有任何问题。
static const int32_t sign_bit = 0x00200000;

int32_t pos_count = (getPosRegisterValue() ^ sign_bit) - sign_bit;