浮点型和实型:实型是数学的说法,只采用十进制,而浮点型则是计算机的表示小数的方法,名称意味着小数点的位置是浮动的.
标准C允许浮点数使用后缀(F or f),比如356f和356.是等价的.
先来明确几个概念:
位模式:计算机中二进制的0、1代码所组成的数字串.
浮点标准: ,在浮点数中,底数一直为2.
尾数(域):也叫有效数,是一个二进制小数,它的范围是[0,1)或[1,2),因为规定规格化值中小数点的左侧必须为1,既然第一位总是1,我们就不需要显式地来表示它了.
(1)当浮点数尾数M=0,无论阶码E为何值,则该浮点数为0值;
(2)当阶码的值遇到比它所能表示的最小值还小时,不管尾数M为何值,则浮点数为0值.
(1)、(2)中的零值称为机器零
移码:一个n位的数中,因为有正数和负数,一定要是2^n或2^n-1才行,才会有移码的特征(负数变成最小的数,才能让我们光从数值上就可以判断其大小),换言之,移码就是127(单精度)或1023(双精度),这样可以让我们直观的看出阶码的大小.
那么为什么移码值是127而不是128呢?假设是128,且此时指数E=127,则e=E+Bias=127+128=255,此时指数全为1,这是无穷大的情况;如果此时指数是0(包括+0和-0),那么e=+0+128,或e=-0(补码)+128=0(溢出),所以当指数E=0时,存在非规格化数.
由于8位E中,只有7位有效数字,7位能表示的大小为0-127,所以偏移量可以在0-127中任取一值,在IEEE754中规定取127.
标准4字节浮点型(单精度型)在计算机里的存储方式如下图(以下所有的位都是二进制位,即01二进制串):
浮点数的具体格式如下:
| 符号域 | 指数域 | 小数域 | 指数偏移量 |
单精度浮点数 | 1 位[31] | 8位[30-23] | 23位[22-00] | 127 |
双精度浮点数 | 1 位[63] | 11 位[62-52] | 52 位[51-00] | 1023 |
一.规格化值:
小数点的左侧必须为1,这样就可以腾出一个二进制位来保存更多的尾数.
当exp不全为0(数值0)或全为1(单精度数值是255,双精度是2047)时.
在这种情况下,指数域解释为表示偏置形式的有符号整数,
举例:
1.已知一个单精度浮点数用16进制数表示为:0xC0B40000,求此浮点数所表达的实数.
先转换为二进制形式(注意:对于负数二进制补码转换成十进制一定要:先取反,后加1)
C0B40000
1100 0000 1011 0100 0000 0000 0000 0000(32位)
按照浮点数格式切割成相应的域 1 1000 0001 01101 000000000000000000
经分析:符号域1 意味着负数;指数域为129 意味着实际的指数为2 (减去偏差值Bias 127);尾数域为01101意味着实际的二进制尾数为1.01101 (加上隐含的小数点前面的1).所以,实际的实数为:
= -1.01101 × 2^ 2=- ( 1*2^0 + 1*2^(-2) + 1*2^(-3) + 1*2^(-5) ) ×2^2
= -(1+0.25+0.125+0.03125)*4
= -1.40625*4 = -5.625.
2. 将实数-9.625变换为相应的浮点数格式.
1) 求出该实数对应的二进制:1001.101,用科学技术法表达为:-1.001101 ×2^3;
2) 因为负数,符号为1;
3) 指数为3,故指数域的值为3 + 127 = 130,即二进制的10000010;
4) 尾数为1.001101,省略小数点左边的1后为001101,右侧0补齐,补够23位,
最终尾数域为:00110100000000000000000;
5) 最终结果:1 1000001000110100000000000000000,用16进制表示:0xC11A0000.
验证代码如下(关于强制指针转换的知识点见上篇):
#include<stdio.h>
#include<stdlib.h>
int main()
{
float f =-9.625;
printf("0x%x", *(int*)&f);//打印0xC11A0000 (机器级的表示)
return 0;
}
注:代码来自:http://blog.csdn.net/justheretobe/article/details/7703575
二.非规格化值:指数域全为0时所表示的数,即
为什么要使用非规格化值呢?一是为了表示数值0而设的,因为规格化数的M>=1(在规格化数值表示法中的M是隐含以1为开头的表示),因此我们无法表示0.实际上,+0.0的浮点表示的位模式为全0:符号位是0,指数域全为0(表示是一个非规格化值),而小数域也全为0.二是为了用来表示那些非常接近域0.0的数.它们提供了一种属性,称为逐渐溢出,其中,可能的数值分布均匀的接近于0.0.
验证代码如下:
#include<stdio.h>
#include<stdlib.h>
typedef struct _float
{
intw:2; //表示i只有一个bit位有效.如果后面紧跟有:int b:1;则a和b会共用一个int
intj:8; //另外结构体在内存中是从低位开始存储的,理解内存这一点对本程序至关重要
ints:1;
}Float;
int main()
{
float f =0;
Floatobj;
obj.s =0; //若等于1呢?结果为-0.000000
obj.j =0;
obj.w=0x400000; //表结构体所占内存的值为000000000 01000000000000000000000
f =*(float*)(&obj);
printf("%f",f); //输出0.000000 (f=+0.01*2^(1-127))——无限接近0
return0;
}
三.无穷大
符号位:1bits | 阶码:8bits | 尾数:23bits |
x | 1111 1111 | 0000 0000 0000 0000 0000 000 |
代码示例:
#include<stdio.h>
#include<stdlib.h>
typedef struct _float4.NON
符号位:1bit | 阶码:8bits | 尾数:23bits |
x | 1111 1111 | 非全0 |
代码示例:
<p align="left"><span style="background-color: rgb(240, 240, 240);">#include<stdio.h></span></p><p align="left"><span style="background-color: rgb(240, 240, 240);">#include<stdlib.h></span></p>typedef struct _float
{
int w:23;
int j:8;
int s:1;
}Float;
int main()
{
float f = 0;
Float obj;
obj.s = 0;
obj.j = 0xff;
obj.w = 0x1;
f = *(float*)(&obj);
printf("%f", f); //输出nan
return 0;
}
注1:
注2:
浮点寄存器一般采用一种特殊的80位的扩展精度格式,以使得精度更高,范围更广。所有的单精度和双精度数在从存储器加载到浮点寄存器中时,都会转换成这种格式,运算总是以扩展精度格式来进行的,当数字存储在存储器中时,它们就从扩展精度转换成单精度或者双精度格式。这样的特性有时候会产生奇怪的结果(深入理解寄存器和存储器的区别——调用函数的栈就是在存储器中,而寄存器当前只能存储需要的少量值)
注3:
浮点型数据不能直接表示大小,这是没有意义的,但是可以用诸如fabs(a-b)<1e-5(1*10^(-5))的方法来进行条件判断。
总结:大多数机器对整数使用二进制补码编码,而对浮点数使用IEEE编码。