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位无符号整数文字时,将执行以下步骤:
- 您将从16位转换为32位,因此它将首先将现有文本扩展为32位。 由于它是一个带符号的文字,因此它首先符号扩展为32位有符号值。
- 既然操作数具有与所需类型相同的位宽,编译器会将其转换为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_t
→ int64_t
→ uint64_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);