float存储格式及FPU
float存储格式及FPU
float存储格式及FPU
浮点数用科学计数法的形式存储, 即分成符号位, 底数位和指数位
如 10.0 的二进制表示为 1010.0, 科学表示法表示为: 1.01exp110, 即 (1+0*1/2+1*1/4)*2^3. 小数点每左移一位指数要加1, 每右移一位指数要减1.
其存储格式符合IEEE标准, 即
数据格式符号位底数位指数位
单精度float    1        23          8
双精度double    1        52        11
扩展精度  1        64        15
存储顺序为: 符号位指数位底数位, 由于底数的个位必为1, 因此个位和小数点就不存储. 指数位的高位也是符号位, 不过为1表示正, 0表示负.
float 因有 8 bits, 所以能表示的有 2 的 256 次方,
但因為指數應可正可負,
所以 IEEE 規定, 此處算出的次方須減去 127 才是真的指數,
所以 float 的指數可從-126 到 128.
double 型態有 11 bits, 算出的值須減去 1023,
所以double 的指數可從 -1022 到 1024.
底数(mantissa):
〈特例〉0  不能以 2 的次方表示
float : 00 00 00 00
double: 00 00 00 00 00 00 00 00
由此可推断浮点数的精度. 单精度的底数只存储23位, 即最小一位为 1/2^23, 故精度为 1/2^23 = 1.19209e-7, 可精确到小数点后6位; 双精度的底数存储52位, 最小位为 1/2^52, 精度为1/2^52 = 2.22045e-16, 能精确到小数点后15位.
双精度的最大值约为 2*2^(2^10-1), 约为1.79e308
关于FPU:
(gdb) list 1,80
1        #include <stdio.h>
2        int main(int argc,char **argv)
3        {
4        float val = 1000;
5        int  ival = val;
6        printf("%f\n",val);
7        printf("%d\n",ival);
8        }
(gdb) display/i $pc
(gdb) break main
Breakpoint 1 at 0x8048365: file float.c, line 4.
(gdb) r
Starting program: /home/lsm1982/float
Breakpoint 1, main () at float.c:4
4        float val = 1000;
1: x/i $pc  0x8048365 <main+17>:        mov    $0x447a0000,%eax (gdb) si
float几个字节多少位
0x0804836a      4        float val = 1000;
1: x/i $pc  0x804836a <main+22>:        mov    %eax,0xfffffff4(%ebp) (gdb)
5        int  ival = val;
1: x/i $pc  0x804836d <main+25>:        flds    0xfffffff4(%ebp) (gdb)
0x08048370      5        int  ival = val;
1: x/i $pc  0x8048370 <main+28>:        fnstcw 0xffffffea(%ebp) (gdb)
0x08048373      5        int  ival = val;
1: x/i $pc  0x8048373 <main+31>:        movzwl 0xffffffea(%ebp),%eax (gdb)
0x08048377      5        int  ival = val;
1: x/i $pc  0x8048377 <main+35>:        mov    $0xc,%ah
(gdb)
0x08048379      5        int  ival = val;
1: x/i $pc  0x8048379 <main+37>:        mov    %ax,0xffffffe8(%ebp) (gdb)
0x0804837d      5        int  ival = val;
1: x/i $pc  0x804837d <main+41>:        fldcw  0xffffffe8(%ebp) (gdb)
0x08048380      5        int  ival = val;
1: x/i $pc  0x8048380 <main+44>:        fistpl 0xfffffff8(%ebp)
(gdb)
0x08048383      5        int  ival = val;
1: x/i $pc  0x8048383 <main+47>:        fldcw  0xffffffea(%ebp) (gdb)
6        printf("%f\n",val);
1: x/i $pc  0x8048386 <main+50>:        flds    0xfffffff4(%ebp)
看来强制类型转换并不只是将存储的
的二进制代码进行不同类型的解释,还有其他可能的情况。
FPU也有其在处理器中的特殊性
5的二进制是101,即1.01(二进制)×2^2 [所有相关帖子]
Float 格式数据长32 bits,最高位为符号位:0为正,1为负;紧接着的8位为阶码:为了便于比较大小,其固定偏移7FH长,即0实际表示-7FH,7FH实际表示0,0FFH实际表示80H;余下的低23位为尾数(有效数字),为了使有效数字达到最大精度,这23个有效数字隐含着固定位1[注2],比如尾数 10000000000000000000001其实就是
1.10000000000000000000001,1被省略,而小数点固定在首位。
符号位 0
阶码0x7F + 2 = 0x81,即二进制的 10000001
尾数 01000000000000000000000 (应该是1.01000000000000000000000,其中1.是缺省具有的,上面说过了)合起来就是0 10000001 01000000000000000000000,即16进制的 40A00000
0x7F + 2 = 0x81 为什么加2?<;无内容> - [bl] 2009-5-7 10:22:50 ( 0 字节, 点击:2 )
2的二次方嘛,+2 就是指“二次方” <;无内容> - [周星星] 2009-5-7 10:25:36 ( 0 字节, 点击:2 )
2的二次方怎么来的?<;无内容> - [bl] 2009-5-7 10:26:35 ( 0 字节, 点击:1 )
to bl:《科学计数法》503表示为5.03E+02,0.7表示为7.0E-01,7表示为7.0E+00 <;无内容> - [周星星] 2009-5-7 10:27:41 ( 0 字节, 点击:3 )
上学的时候学过,现在全都忘光:)<;无内容> - [jbisjb] 2009-5-7 10:31:17 ( 0 字节, 点击:0 )
同样,二进制的101用科学计数法表示就是1.01E+02 <;无内容> - [周星星] 2009-5-7 10:28:16 ( 0 字节, 点击:2 )
同时,因为二进制只有0和1,而科学计数法第一位都是非0,即二进制的科学计数法第一位都是1.,既然都是1.,那么自然可以省略掉<;无内容> - [周星星] 2009-5-7 10:29:34 ( 0 字节, 点击:2 )
因为省略了1.,所以float/double其实是无法表示0.0的,这时候…… <;无内容> - [周星星] 2009-5-7 10:30:33 ( 0 字节, 点击:4 )
这时候就用 1.0E负最大来表示 +0.0,用 -1.0E负最大来表示 -0.0 <;无内容> - [周星星] 2009-5-7 10:31:50 ( 0 字节, 点击:2 )
恩恩,听起来好像有什么复杂的隐情哦<;无内容> - [bl] 2009-5-7 10:32:28 ( 0 字节, 点击:0 ) 语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。
无论是单精度还是双精度在存储中都分为三个部分:
1. 符号位(Sign) : 0代表正,1代表为负
2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储(后面将详细说明)
3. 尾数部分(Mantissa):尾数部分
其中float的存储方式如下图所示:
而双精度的存储方式为:
R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25*,而120.5可以表示为:1.205*, 这
认识0,1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示,8.25用二进制表示可表示为1000.01,我靠,不会连这都不会转换吧?那我估计要没辙了。120.5用二进制表示为:11101
10.1用二进制的科学计数法表示1000.01可以表示为1.0001*,1110110.1可以表示为1.1101101*,任何一个数都的科学计数法表示都为
<*, 尾数部分就可以表示为xxxx,第一位都是1嘛,干嘛还要表示呀?可以将小数点前面的1省略,所以23bit
的尾数部分,可以表示的精度却变成了 24bit,道理就是在这里,那24bit能精确到小数点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数点,24bit就能使float能精确到小数点后6位,而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了,所以指数部分的存储采用移位存储,存储的数据为元数据+127。
例如,我们要想偷窥浮点类型的值4.25在计算机硬盘中存储的庐山真面目,请跟我来:首先把4.25转换成二进制的表达方式,即100.01,在详细点,变成1.0001x22,好了,对号入座把。
Sign=0;
Exponent(bias)=2+127=129 (偏移量为127,就是直接加上个127了);
Mantissa=1.0001-1.0=0001(规格化后,小数点前总是整数1,全世界人都知道前面是1不是0,所以省略不写了,即尾数部分不包括整数部分;当别人问你,为什么23 bit的尾数部分可以表示24位的精度,
知道怎么回答了吧。
下面就看看8.25和120.5在内存中真正的存储方式。
首先看下8.25,用二进制的科学计数法表示为:1.0001*
按照上面的存储方式,符号位为:0,表示为正,指数位为:3+127=130 ,位数部分为,故8.25的存储方式如下图所示:
而单精度浮点数120.5的存储方式如下图所示:
那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:
根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101*=120.5
而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的
下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。