使用 avr-gcc 隐式转换为 float:uint8_t 与 uint16_t

Implicit conversion to float using avr-gcc: uint8_t vs. uint16_t

我有一个关于使用 Arduino IDE 1.8.2 (gcc 4.9.2.) 隐式转换 uint8_tuint16_t 的问题。硬件是标准的 Arduino (ATMega328p)。

我用 uint8_t 写了一段代码,后来决定改用 uint16_t。 (应该已经看到了……)

但是,对于隐式转换,它们的行为似乎略有不同,这是导致程序出错的原因。

一个最小的工作示例如下:

void setup()
{
  uint8_t x = 15;
  uint8_t y = 5;
  float myF = y-x;

  Serial.begin(74880);
  Serial.println(myF);
}

这将在我的串行控制台上打印 -10.00。
这很好,也是我所期望的。

但是,如果我将 x(或 xy)更改为 uint16_t,结果将是 65526.00! 如果我将 myF 从 float 更改为 int,我会再次得到 -10。 (我从不更改任何值)

当我将结果存储在带符号的数据类型中时,我假设编译器意识到负值和 "handles the situation correctly" 的可能性(保留符号,就像在 int 情况下一样)或在中打印警告如果它对不匹配的数据类型不满意。但是,即使将警告级别设置为 "all",它也从未显示警告。所以我假设编译器知道如何在不丢失 sign/data.

的情况下处理这种情况

此外,由于它使用 int 作为目标数据类型,令我惊讶的是它不适用于更大的浮点数。

我已经在我的x86 系统上测试了这种情况- gcc 4.7.3 保留了标志。然而,在 AVR 的 8 位微控制器世界中,不同的 rules/conditions 可能适用。 (?)

那么那里发生了什么?也许具有更多编译器知识的人可以在这里提供帮助..
(我知道我可以通过显式转换来避免这种情况,但因此我必须意识到这个陷阱。
所以我想知道到底是什么原因造成的,因为当从 uint8_t 切换到 uint16_t 时真的很惊喜..)

我已经根据 integer conversion rules "integer types smaller than int are promoted to int when an operation is performed on them" 阅读了该内容。我假设 avr-gcc 遵循整数提升。 (?) 所以我知道实际计算通常在 int 上运行,然后转换为目标数据类型(在本例中为 float)。这里的问题是 uint16_t 与 AVR 的 16 位 int 相等但大小不小,因此无法提升 uint16_t 吗?如果是这样,为什么它使用 int 作为目标类型?

为什么它使用 int 作为目标变量,而不是 4 字节浮点数? 为什么它不发出警告?

使用无符号整数变为负值将这些无符号类型转换为浮点数,这是未定义实现定义的行为。使用 int8_t 或 int16_t 来正确处理负值。从 uint16_t 转换为浮动与从 uint8_t 转换的行为差异取决于实现,因此很难准确判断发生了什么。

So what is going on there?

整数促销

If an int can represent all values of the original type ..., the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.
C11 §6.3.1.1 2

在下面,y-x 将每个 x,y 提升为 int 并计算 5-15 即 int -10 并将该值分配给 mF

  uint8_t x = 15;
  uint8_t y = 5;
  float myF = y-x;

在下面,y-x 将每个 x,y 提升为 unsigned 并计算 5u-15u 即 unsigned 65526u 并将该值分配给 mF

  uint16_t x = 15;
  uint16_t y = 5;
  float myF = y-x;

为什么是 unsigned 而不是 intuint16_t 在 16 位 int 平台上升级 uint16_t 时不满足“如果 int 可以表示原始类型的所有值”条件。

没什么神秘的,只是 整数促销 在具有 16 位 int/unsigned

的平台上