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

查看:61
本文介绍了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

希望从左边移入零.事实证明,正确的无符号整数表达式的移位规则不同,因此您可以使用一种类型转换,用于抑制从左侧复制的内容.

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 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天全站免登陆