C语言 使用uint16_t操作时未显式转换为float时出现意外结果

oxf4rvwz  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(247)

我正在做一些操作,没有找到确切的解释,为什么我发现一个特定的行为。内容:

  • 我接收一个值(24.2),然后计算一些偏移和增益
  • 然后通过CAN发送结果。

最低工作示例:

#include <stdio.h>
#include <stdint.h>

int main()
{
    printf("Operations with comas \n");
    
    uint16_t a = (uint16_t)((24.2 - 0)/0.1);        /* 241  Incorrect*/
    uint16_t b = (uint16_t)((24.2 - 0.0)/0.1);      /* 241 Incorrect */
    uint16_t c = (uint16_t)((float)(24.2 - 0)/0.1); /* 242 Correct */
    uint16_t d = (uint16_t)(24.2/0.1);              /* 241 Incorrect*/
    uint16_t e = (uint16_t)(242.0);                 /* 242 Correct */
    
    printf("a %u \n" , a);
    printf("b %u \n" , b);
    printf("c %u \n" , c);
    printf("d %u \n" , d);
    printf("e %u \n" , e);
    
    return 0;
}

我知道当使用float时,值不能精确地表达。对于IEEE-75424.2 is的值实际上代表24.200000762939453125,因此ce的截断是正确的。
为什么abd会产生意想不到的值?(我知道强制演员阵容可以解决问题,但我想了解原因)

iaqfqrcu

iaqfqrcu1#

常数24.2的类型为double,其值约为24.1999999999993。所以除以0.1得到大约241.99999999999716,将此值转换为整数类型会截断为241。
当您将24.2转换为float类型时,最接近的表示是24.2000007629394531,除以0.1得到242.0000076293945312,它被截断为242。

omqzjyyz

omqzjyyz2#

就像1/3是十进制中的周期数一样,
2/10是二进制的周期数,
1/10是二进制中的周期数。
这些将需要无限的存储来准确地存储为浮点数。
因此,24.2和0.1不能准确地表示为浮点数。
因此,一个接一个地潜水可能不会产生完全正确的结果。它可能稍微太大或稍微太小。
因此,截断可能具有不期望的效果。
在对double使用IEEE双精度数的机器上,

24.2       = 24.199999999999999289457264239899814128875732421875
0.1        = 0.1000000000000000055511151231257827021181583404541015625
24.2 / 0.1 = 241.999999999999971578290569595992565155029296875

当你使用float而不是double(浮点常量的默认值)时,你很幸运。它恰好是一个比你想要的稍大的数字。

24.2f       = 24.200000762939453125
0.1         = 0.1000000000000000055511151231257827021181583404541015625
24.2f / 0.1 = 242.00000762939453125

但它也很容易被压下去。也许你应该使用四舍五入而不是截断?

tcomlyy6

tcomlyy63#

根据C标准,无后缀浮点常量的类型为double,而不是float,因此代码中所有带点的常量都是double,其他所有常量都升级为double
以下面的代码为例(注意第二个表达式值的后缀f):

#include <stdio.h>
#include <inttypes.h>

int main(void) {
    uint16_t a = (uint16_t)((24.2 - 0)/0.1);
    uint16_t b = (uint16_t)((24.2f - 0)/0.1f);
    printf("%hu %hu\n", a, b);
}

输出为:

241 242

这里的要点是,由于使用了不同的浮点精度,所以会发生不同的近似。

4dc9hkyq

4dc9hkyq4#

如果实现定义了__STDC_IEC_559__,浮点运算需要符合规范的附录F。尤其应注意C11 F.3中的以下项目:

  • +-*/运算符提供IEC 60559的加、减、乘和除运算。
  • 浮点类型的转换提供了浮点精度之间的IEC 60559转换。
  • 从整数到浮点类型的转换提供了从整数到浮点的IEC 60559转换。
  • 从浮点型到整数型的转换提供了类似IEC 60559的转换,但总是向零舍入。

在这种情况下,编译器根据“as-if”规则执行的任何优化都需要考虑上述因素,包括每个操作的舍入误差。它不能仅仅使用24.2 / 0.1 = 242的数学事实。

相关问题