关于c ++:使用avr-gcc隐式转换为浮点型: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。 (应该已经看到了……)

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

最小的工作示例如下:

1
2
3
4
5
6
7
8
9
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。 (我从不更改任何值)

当我将结果存储为带符号的数据类型时,我假设编译器意识到了负值的可能性,并"正确处理了情况"(保留符号,与int情况一样),或者在警告不存在时打印警告。对于数据类型不匹配感到高兴。但是,即使将警告级别设置为"全部",也不会显示警告。因此,我假设编译器知道如何处理这种情况而不会丢失符号/数据。

另外,由于它可以将int作为目标数据类型使用,所以令我惊讶的是它不适用于较大的float。

我已经在x86系统上测试了这种情况-gcc 4.7.3保留了该标志。但是,在AVR的8位微控制器领域中,可能适用不同的规则/条件。 (?)

那到底是怎么回事?也许有更多编译器知识的人可以在这里提供帮助。
(我知道我可以通过显式强制转换来避免这种情况,但是因此,我不得不意识到这一陷阱。
因此,我想知道到底是什么原因造成的,因为从uint8_t切换到uint16_t时确实感到惊讶。)

我已经读过,根据整数转换规则,"对小于int的整数类型在对其执行操作时会提升为int"。我假设avr-gcc遵循整数促销。 (?)因此,我知道实际的计算通常无论如何都在int上运行,然后转换为目标数据类型(在这种情况下为float)。这里的问题是uint16_t是否相等,但大小不小于AVR的16位int,因此不能提升uint16_t?如果是这样,为什么将int作为目标类型呢?

为何将int作为目标变量而不使用4字节浮点数呢?
为什么不警告呢?


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

1
2
3
  uint8_t x = 15;
  uint8_t y = 5;
  float myF = y-x;

对于以下内容,y-x将每个x,y提升为unsigned,并计算5u-15u(即unsigned 65526u)并将该值分配给mF

1
2
3
  uint16_t x = 15;
  uint16_t y = 5;
  float myF = y-x;

为什么是unsigned而不是int? 在提升uint16_t时,在16位int平台上,uint16_t不满足"如果int可以代表原始类型的所有值"的条件。

毫无疑问,在具有16位int/unsigned的平台上只是整数促销


您正在使用无符号整数去负数,将这些无符号类型强制转换为浮点数,即实现定义的行为 undefined 。 使用int8_t或int16_t正确处理负值。 从uint16_t浮动的转换与从uint8_t转换的转换在行为上的差异取决于实现,因此很难确切说明正在发生什么。