【问题标题】:Implicit conversion to float using avr-gcc: uint8_t vs. uint16_t使用 avr-gcc 隐式转换为浮点数:uint8_t 与 uint16_t
【发布时间】:2018-06-13 16:43:34
【问题描述】:

我有一个关于使用 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。 (我从不更改任何值)

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

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

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

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

根据integer conversion rules“当对它们执行操作时,小于 int 的整数类型被提升为 int”。我假设 avr-gcc 遵循整数促销。 (?)所以我理解实际计算通常在 int 上运行,然后转换为目标数据类型(在这种情况下为浮点数)。这里的问题是uint16_t 与 AVR 的 16 位 int 相同,但大小不小于,因此无法提升 uint16_t?如果是这样,为什么它使用 int 作为目标类型?

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

【问题讨论】:

  • 如果 int 是 16 位,那么 uint16_t 不是“更小”。两个操作数的类型相同,所以减法是用unsigned 完成的,并且值以明确定义的方式包装。
  • 计算与float无关,编译器不会“实现”任何东西”。结果分配给float之后。你会有将一个操作数转换为float计算之前考虑float
  • (是的,我知道 1 年零 7 个月后)但有趣的是 uint8_t 的 -10 的“预期”结果并不是我所期望的。我会说它应该是 246。预计 65526。而“再次-10”也是预期的结果。由于您从未在其他地方使用过 x 和 y,因此可能已经对其进行了优化。

标签: c++ arduino floating-point type-conversion avr-gcc


【解决方案1】:

那么那里发生了什么?

整数促销

如果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 并计算 5u-15u,即 unsigned 65526u 并将该值分配给 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 的平台上整数促销

【讨论】:

  • 嗯,这就是它。 (顺便说一句:int -10 分配给 mF。)在重新审视整数转换规则并记住您的答案和@Weather 的评论后,我现在知道发生了什么。我认为编译器认为一切都会以浮点数结束,但这是一个误解。使用 C 时要考虑的另一件事...总是强制转换为已签名/浮动手动当签名可能成为必要时..或测试得很好...
  • @random 在一般中,应避免强制转换,因为它往往会掩盖错误或弱代码 - 通常有更好的选择。然而在其他时候它是明智的。它非常依赖代码。
【解决方案2】:

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

【讨论】:

  • 这里没有未定义的行为,但是这里的整数转换取决于实现的int 类型是否可以表示uint16_t 的所有值。所以最多是实现定义的行为
  • 为简单起见,我没有提到我的程序运行的数据值总是正值。因此将它们存储在 int16_t 而不是 uint16_t 会降低可能的数据分辨率。 (并且去 int32_t 会使数据集的大小翻倍)正如在计算之前明确地转换变量似乎是要走的路,这对我来说仍然是一个意外的发现,因为我认为目标数据类型是由编译器,我没想到将数据更改为更广泛的数据类型会出现问题。但现在我知道得更多了。
猜你喜欢
  • 1970-01-01
  • 2014-07-23
  • 1970-01-01
  • 2018-05-15
  • 1970-01-01
  • 2015-10-09
  • 2020-10-17
  • 2019-09-18
  • 1970-01-01
相关资源
最近更新 更多