Arduino中不需要的符号扩展

我试图在Arduino中实现逻辑右移(即避免符号扩展),并在阅读Arduino BitShift指南( https://www.arduino.cc/en/Reference/Bitshift )后,它建议将无符号变量转换为对,不会导致签名延期:

当您将x向右移位y位(x >> y),并且x中的最高位为1时,行为取决于x的确切数据类型。 如果x是int类型,则最高位是符号位,确定x是否为负,如上所述。 在这种情况下,由于历史原因,符号位被复制到较低位:

int x = -16; // binary: 1111111111110000 int y = x >> 3; // binary: 1111111111111110 This behavior, called sign extension, is often not the behavior you want. Instead, you may 

希望零从左侧移入。 事实certificate,对于unsigned int表达式,右移规则是不同的,因此您可以使用类型转换来抑制从左侧复制的规则。

在我的测试中,它不能像那样工作:

 Serial.print( ((uint32_t)(1<> 15, BIN); 

打印:

11111111111111111

这意味着,标志扩展正在进行中。 我也从那里尝试了建议的例子,结果相同。

难道我做错了什么? 是否可以进行移位并强制操作是逻辑而不是算术?

首先,我认为你遗漏了一些关键信息:看起来你必须使用具有16位int类型的Arduino板(例如,Arduino Uno)。

这里的问题是整数提升在C和C ++中是如何工作的。 当您将16位有符号整数文字( 1<<15 )转换为32位无符号整数文字时,将执行以下步骤:

  1. 您将从16位转换为32位,因此它将首先将现有文本扩展为32位。 由于它是一个带符号的文字,因此它首先符号扩展为32位有符号值。
  2. 既然操作数具有与所需类型相同的位宽,编译器会将其转换为32位无符号整数类型。

我没有一台16位机器来测试这个,但我可以在我的64位笔记本电脑上用这个测试程序复制相同的行为:

 #include  #include  int main(int argc, const char* argv[]) { printf("%" PRIx64 "\n", ((uint64_t)(1 << 31))); // prints ffffffff80000000 printf("%" PRIx64 "\n", ((uint64_t)(1 << 31)) >> 31); // prints 1ffffffff return 0; } 

所以你可以在这个程序中看到,不是>>操作正在执行不需要的符号扩展,而是从32位有符号整数转换为64位无符号整数,因为转换实际上是这样的:

int32_tint64_tuint64_t

如果你想避免额外的符号扩展,你应该以无符号文字开头(正如一些程序员在他的评论中建议的那样),或者使用相同宽度的无符号类型进行转换。 任何这些应该工作:

 Serial.print( ((uint32_t)(1u<<15)) >> 15, BIN); Serial.print( ((uint16_t)(1<<15)) >> 15, BIN); Serial.print( ((uint32_t)(uint16_t)(1<<15)) >> 15, BIN);