跳转至

float类型中NaN和Inf是什么?

更新日期: 2022-10-2
  • 2022-10-2 更新格式
  • 2022-09-30 创建文档

Cyber & Robot 授权发布

做嵌入式的同学应该知道,c语言中int的大小,是和平台有关的,有的占4字节,有的占2字节。

所以我们对有期望长度的变量,很少直接用int定义,而更多用uint8_t, uint16_t, uint32_t。

但到了float,我们为什么没有float16_t, float32_t这样的类型呢?因为float是有标准的,就是32位占4字节,这就是IEEE-754标准。

IEEE-754标准

简单的说一下吧,说太细很枯燥。IEEE-754标准将float分成了三部分,符号位(S)、指数(E)和尾数(F),一共4字节,32位。

符号位 指数 尾数
S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF
1位 8位 23位
  • 符号位:0为正,1为负。
  • 指数:用补码表示,可表示[-127, 128]的指数。
  • 尾数:用二进制表示小数部分。

好,了解到这里就差不多了。当然这里说的都是float,也就是单精度浮点数。还有双精度浮点数,也就是double。

一般在实际使用中,我们就算在内存中读到浮点数,也不会直接通过16进制转2进制再还原成浮点数,最方便的还是使用在线转换工具啦。

下面进入正题,讲一些特殊值。

零值

指数全为0,尾数全为0,符号位任意,于是有了0和-0,且0==-0。所以当我们使用memset()的时候,是能给float初始化成0的。

符号位 指数 尾数
S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF
0/1 00000000 0000000 00000000 00000000

NaN

指数全为1,尾数非0。

符号位 指数 尾数
S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF
0/1 11111111 1111111 11111111 11111111

NaN是Not A Number的意思,在未定义的算术运算结果会出现,比如x/0。注意,说明是尾数非0,表格中填的全是1,但实际以各平台实现为准,代码判断如下:

if((fnum&0x7F800000)==0x7F800000 && (fnum&0x7FFFFF)!=0) {
    printf("is NaN\n");
}

无穷值(Infinity)

指数全为1,尾数全为0,符号位任意,有正无穷和负无穷。

符号位 指数 尾数
S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF
0/1 11111111 0000000 00000000 00000000

代码判断如下:

if(fnum==0x7F800000) {
    printf("is +Inf\n");
}
else if(fnum==0xFF800000) {
    printf("is -Inf\n");
}

标准c库中的支持

#include <math.h>

void float_test(void)
{
    float num1 = 1.256f;
    float num2 = NAN;
    float num3 = INFINITY;
    float num4 = num1/0;

    printf("num1:%f isnan:%d isinf:%d\n", num1, isnan(num1), isinf(num1));
    printf("num2:%f isnan:%d isinf:%d\n", num2, isnan(num2), isinf(num2));
    printf("num3:%f isnan:%d isinf:%d\n", num3, isnan(num3), isinf(num3));
    printf("num4:%f isnan:%d isinf:%d\n", num4, isnan(num4), isinf(num4));
}

最后多说几句

有一个有趣的地方,上面的示例中用了printf打印%f,首先很多嵌入式工程都不推荐使用c库实现的printf,然后是各种各样的其他库提供了printf的实现。

然后呢,有的库其实不支持%f的格式,毕竟在有的芯片上,浮点数都是要求尽量少使用的。

再然后,有的库支持了浮点打印,但对NaN、无穷数,并不能打印出"NaN / Inf"之类的字样。

而会打印成一个看起来奇怪的浮点数,但是放在一堆混乱的数据里,可能也不显得奇怪了。

所以大家可以在自己常用的项目平台上试试上面的代码,看看printf的支持程度怎么样。

当然,以后有空我也会出一篇文章专门讲怎么实现printf的。就比如想偷懒,以为引入SEGGER_RTT_printf就能解决调试信息打印的问题,却发现不支持浮点打印,也是挺遗憾的。

printf("关注这里,获取首发 segmentfault.com/u/yinyineal/articles \n");

本站说明

一起在知识的海洋里呛水吧。广告内容与本站无关。如果喜欢本站内容,欢迎投喂作者,谢谢支持服务器。

AndroidTutorial - Gitee AndroidTutorial - Github 反馈问题 最近更新 投喂作者

Ads