做嵌入式的同学应该晓得,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");