Arduino中不需要的符号扩展 [英] Unwanted sign extension in Arduino

查看:132
本文介绍了Arduino中不需要的符号扩展的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在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:

当您将x向右移y位(x >> y)时,x中的最高位是 a 1,其行为取决于x的确切数据类型.如果x是类型 int,最高位是符号位,确定x是否为 正如我们上面讨论的,是否定的.在这种情况下, 出于深厚的历史原因,将位复制为低位:

When you shift x right by y bits (x >> y), and the highest bit in x is a 1, the behavior depends on the exact data type of x. If x is of type int, the highest bit is the sign bit, determining whether x is negative or not, as we have discussed above. In that case, the sign bit is copied into lower bits, for esoteric historical reasons:

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);

打印:

11111111111111111

11111111111111111

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

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:

  1. 您要从16位转换为32位,因此它将首先将现有文字扩展到32位.由于它是带符号的文字,因此首先将其符号扩展为32位带符号的值.
  2. 现在,操作数具有与所需类型相同的位宽,编译器将其强制转换为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_tint64_tuint64_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中不需要的符号扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆