问题描述
我正在尝试在Arduino中实现逻辑右移(即避免符号扩展),并且在阅读了Arduino BitShift指南( https://www.arduino.cc/en/Reference/Bitshift ),这表明将无符号变量向右移动不会引起符号扩展:
I am trying to achieve logical right shift in Arduino(i.e. avoid sign extension), and after reading the Arduino BitShift guide (https://www.arduino.cc/en/Reference/Bitshift), it suggests that shifting unsigned variables to the right, wont cause sign extension:
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
希望零从左边移入.原来,正确的 移位规则对于无符号int表达式是不同的,因此您可以使用 禁止从左侧复制副本的类型转换.
wish zeros to be shifted in from the left. It turns out that the right shift rules are different for unsigned int expressions, so you can use a typecast to suppress ones being copied from the left.
在我的测试中,它不是那样工作的:
In my tests, it doesn't work like that:
Serial.print( ((uint32_t)(1<<15)) >> 15, BIN);
打印:
这意味着,标志扩展正在进行中.我也从那里尝试了建议的示例,结果相同.
Which means, there's sign extension going on.I've also tried the suggested example from there, with the same results.
我做错什么了吗?还有可能进行移位并强制运算为逻辑运算而不是算术运算吗?
Am I doing something wrong?Also Is it possible to do a shift and force the operation to be logical instead of arithmetic?
推荐答案
首先,我认为您遗漏了一些关键信息:看来您必须使用16位int
类型的Arduino板(例如, Arduino Uno).
First, I think you left out some key information: it looks like you must be using an Arduino board with a 16-bit int
type (e.g., Arduino Uno).
这里的问题是整数提升如何在C和C ++中工作.将16位带符号整数文字1<<15
转换为32位无符号整数文字时,将执行以下步骤:
The problem here is how integer promotion works in C and C++. When you cast a 16-bit signed integer literal, 1<<15
, to a 32-bit unsigned integer literal, it will take the following steps:
- 您要从16位转换为32位,因此它将首先将现有文字扩展到32位.由于它是带符号的文字,因此首先将其符号扩展为32位带符号的值.
- 现在,操作数具有与所需类型相同的位宽,编译器将其强制转换为32位无符号整数类型.
我没有16位计算机在附近进行测试,但是我可以使用此测试程序在64位笔记本电脑上复制相同的行为:
I don't have a 16-bit machine sitting around to test this on, but I can duplicate the same behavior on my 64-bit laptop with this test program:
#include <stdio.h>
#include <inttypes.h>
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位无符号整数的转换,因为转换实际上就像这个:
So you can see in this program, it's not the >>
operation that's doing the unwanted sign extension, but rather the cast from a 32-bit signed integer to a 64-bit unsigned integer, because the cast actually acts like this:
int32_t
→int64_t
→uint64_t
如果要避免多余的符号扩展名,则应以无符号文字开头(如某些程序员在其注释中建议的那样),或强制转换为相同宽度的无符号类型.其中任何一个都应该起作用:
If you want to avoid the extra sign extensions, you should either start with an unsigned literal (as Some programmer dude suggested in his comment), or make a cast to the same-width unsigned type. Any of these should work:
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);
这篇关于Arduino中不需要的符号扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!