目录
[1.printf("n的值为:%d\n", n);](#1.printf("n的值为:%d\n", n);)
[2.printf("*pFloat的值为:%f\n", *pFloat);](#2.printf("*pFloat的值为:%f\n", *pFloat);)
[3.printf("num的值为:%d\n", n);](#3.printf("num的值为:%d\n", n);)
[4.printf("*pFloat的值为:%f\n", *pFloat);](#4.printf("*pFloat的值为:%f\n", *pFloat);)
1.浮点数的类型
float,double,long double等等
2.浮点数表示的范围
在float.h中介绍
以下截取一部分
cpp
#define FLT_MAX 3.402823466e+38F // max value
#define FLT_MAX_10_EXP 38 // max decimal exponent
#define FLT_MAX_EXP 128 // max binary exponent
#define FLT_MIN 1.175494351e-38F // min normalized positive value
#define FLT_MIN_10_EXP (-37) // min decimal exponent
#define FLT_MIN_EXP (-125) // min binary exponent
FLT_MAX是float类型的最大值,FLT_MIN是float类型的最小值
3.浮点数的特性
浮点数在内存中无法精确保存
《计算机科学导论》的叙述
摘抄图
4.浮点数在内存中的存储
求下列代码的执行结果
cpp
#include <stdio.h>
int main()
{
int n = 9;
//&n被强制类型转换
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
//整型数据以浮点型打印
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
答案速查
分析
前置知识:浮点数的存储规则
对《计算机科学导论》的浮点数总结后:
根据国际标准IEEE(电气和电子工程协会) 754(简称7534标准),任意一个二进制浮点数V可以表示成下面的形式:
如十进制+5.5==101.1==1.011*(此时S==0,M==1.011且1<M<2,E==2)
754标准的特殊规定
1.由于M总是可以写成1.??????的形式,因此,在计算机内部保存M时,默认这个数的第一位总是
1,因此可以被舍去,只保存后面的??????部分。比如保存1.01的时候,只保存01,等到读取的时
候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有
23位,将第一位的1舍去以后,等于可以保存24位有效数字
2.指数E
IEEE规定:E为无符号整数(unsigned int)
但在科学计数法中的E是可以出现负数的,因此IEEE修正为:
存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023
例如:在32位中,E为8位:-2-->-2+127-->125-->补码为0111 1101
在x64+debug环境下,将下列代码调试,下断点至return 0;
cpp
#include <stdio.h>
int main()
{
float f = 5.5;
return 0;
}
推导单精度浮点数5.5在内存中的存储
float是单精度(32位),double是双精度(64位)-->这里的32位和64位与电脑的运行环境没有关系!!
十进制5.5按IEEE标准写法:
现按照"S+E+M"的形式存储
S=0,E为2,2+127=129=1000 0001,M=1.011
舍去M的第一位,并补满23位
最终为 0 10000001 011000000000000000000004
转换为十六进制:0100 0000 1011 0000 0000 0000 0000 0000
-->40 B0 00 00
验证
x86+debug环境下,打开内存窗口,输入&f
f在内存中存储为00 00 b0 40(小端序存储)
浮点数取出的分析
1.一般情况:E不全为0或不全为1
按存储规则反过来还原即可
2.特殊情况:E全为0
指数E等于1-127(或1-1023)
注意:这里不是0-127或0-1023!如果E为0,则权为,这样就不能表示小数!则下方所说的极小的小数也就不成立了
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的极小的小数(权为)(趋近于+0或-0)
3.特殊情况:E全为1
11111111b=255-->255-127=128(权为2的128次方),表示+∞或-∞(正负取决于符号位S)
回到本题分析
1.printf("n的值为:%d\n", n);
整型数字以整型打印,为9
2.printf("*pFloat的值为:%f\n", *pFloat);
整型9以单精度浮点打印
整型9在内存中的存储为09 00 00 00
-->把小端序换为正常的表示顺序 0000 0000 0000 0000 0000 0000 0000 1001
-->按按照"S+E+M"的形式重新排列
0 0000000 00000000000000001001
S=0,E全为0-->2的-126次方-->
说明浮点数极小-->被忽略-->输出0.000000(默认保留小数前6位)
3.printf("num的值为:%d\n", n);
9.0-->S=0(正数:原码=反码=补码),E=,M=1.001-->3+127=130=1000 0010b
0 10000010 00100000000000000000000
按整型的形式排列:0100 0001 0001 0000 0000 0000 0000 0000
对应十六进制:41 10 00 00-->小端序存储-->在内存中排布为01 00 00 41
4.printf("*pFloat的值为:%f\n", *pFloat);
单浮点数以单浮点形式打印,为9.0
5.其他注意事项
有写十进制浮点数转换成二进制会有偏差
如3.14
二进制只能表示这样的数
具体转换的方式见《计算机科学导论》
所以在比较浮点数时可能会出现错误
复制以下代码到不同的编译器上测试
cpp
#include <stdio.h>
int main()
{
if (3.14 == 3.12 + 0.2)
{
printf("==");
}
else
{
printf("!=");
}
}
VS2022
VS2019
Dev C++ TDM-GCC 4.9.2
结果都一样且正确
复制以下代码到不同的编译器上测试
cpp
#include <stdio.h>
int main()
{
float a = 0.3;
float b = 0.2;
float c = 0.1;
if (a == b + c)
{
printf("==");
}
else
{
printf("!=");
}
}
VS2022
VS2019
Dev C++ TDM-GCC 4.9.2
结果不一样,不同的编译器的精度不一样
浮点数的判断有专门的写法:比差值+规定误差(精度)
cpp
#include <stdio.h>
#include <math.h>
int main()
{
if (fabs(3.12 + 0.2 - 3.14)<0.2)
{
printf("==");
}
else
{
printf("!=");
}
}
函数fabs为求浮点数绝对值函数,需要写 #include <math.h>