使用 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从浮点数更改为 int,我会再次得到 -10。(我从不更改任何值(

当我将结果存储在有符号数据类型中时,我假设编译器意识到负值的可能性并"正确处理情况"(保留符号,就像在 int 情况下一样(或打印警告以防它对不匹配的数据类型不满意。但是,即使将警告级别设置为"全部",它也从未显示警告。所以我假设编译器知道如何在不丢失符号/数据的情况下处理这种情况。

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

我已经在我的 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 字节浮点数? 为什么它不警告?

那么这是怎么回事呢?

整数促销

如果一个int可以表示原始类型的所有值...,则该值将转换为int;否则,它将转换为unsigned int。这些称为整数促销.
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并计算unsigned 65526u的 5u-15u 并将该值分配给mF

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

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

没有神秘感,只是在具有 16 位int/unsigned的平台上进行整数促销

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

最新更新