提问人:Ricard Molins 提问时间:9/7/2023 最后编辑:wohlstadRicard Molins 更新时间:9/10/2023 访问量:85
在未显式转换为浮动的情况下使用 uint16_t 操作时出现意外结果
Unexpected result when operating with uint16_t without explicit casting to float
问:
我正在做一些操作,但没有找到确切的解释来解释为什么我发现特定行为。 上下文:
- 我收到一个值 (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;
}
我知道当使用浮点数时,该值无法准确表示。用实际表示的值,因此在大小写和中截断是正确的。IEEE-754
24.2 is
24.200000762939453125
c
e
为什么 case ,, 会产生意想不到的值?(我知道强制施法可以解决问题,但我想解释原因)a
b
d
答:
根据 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
所以这里的重点是,由于使用了不同的浮点精度,所以正在发生不同的近似值。
评论
常量的类型为 ,值约为 24.19999999999999993。因此,除以 0.1 大约得到 241.999999999999999716,并将此值转换为整数类型将截断为 241。24.2
double
当您转换为 类型 时,最接近的表示形式是 24.2000007629394531,除以 0.1 得到 242.0000076293945312,它被截断为 242。24.2
float
就像 1/3 是十进制的周期数,2/10 是二进制的周期数,
1/10 是二进制的周期数。
这些将需要无限存储才能准确地存储为浮点数。
因此,24.2 和 0.1 不能准确地表示为浮点数。
因此,一个接一个地潜水可能不会产生完全正确的结果。它可能略微过大或略小。
因此,截断可能会产生不良影响。
在使用 IEEE 双精度数字的计算机上,将double
24.2 = 24.199999999999999289457264239899814128875732421875
0.1 = 0.1000000000000000055511151231257827021181583404541015625
24.2 / 0.1 = 241.999999999999971578290569595992565155029296875
当您使用 a 而不是 a(浮点常量的默认值)时,您很幸运。它恰好是一个比你想要的数字略大的数字。float
double
24.2f = 24.200000762939453125
0.1 = 0.1000000000000000055511151231257827021181583404541015625
24.2f / 0.1 = 242.00000762939453125
但它也很容易被低估。也许你应该使用舍入而不是截断?
如果实现定义,浮点运算需要符合规范的附录 F。特别应注意 C11 F.3 中的以下项目:__STDC_IEC_559__
- 、、 和运算符提供 IEC 60559 加法、减法、乘法和除法运算。
+
-
*
/
- 浮点类型的转换提供浮点精度之间的 IEC 60559 转换。
- 从整数到浮点类型的转换提供了从整数到浮点的 IEC 60559 转换。
- 从浮点型到整数型的转换提供类似于 IEC 60559 的转换,但始终向零四舍五入。
在这种情况下,编译器根据“假设”规则执行的任何优化都需要考虑上述注意事项,包括每个操作的舍入误差。它不能仅仅使用 24.2 / 0.1 = 242 的数学事实。
评论
double
float
double
double