IEEE754浮点数的表示方法

1.浮点数的存储格式

浮点数(Floating-point Number)是一种对于实数的近似值数值表现法,由一个有效数字(即尾数)加上幂数来表示,通常是乘以某个基数的整数次幂得到。以这种表示法表示的数值,称为浮点数。表示方法类似于基数为10的科学计数法。利用浮点进行运算,称为浮点计算,这种运算通常伴随着因为无法精确表示而进行的近似或舍入。

计算机对浮点数的表示规范遵循电气电子工程师协会(IEEE)推出的IEEE 754标准,浮点数在C/C++中对应float和double类型,我们有必要知道浮点数在计算机中实际存储的内容。

IEEE754标准中规定float单精度浮点数在机器中表示用 1 位表示数字的符号,用 8 位来表示指数,用23 位来表示尾数,即小数部分。对于double双精度浮点数,用 1 位表示符号,用 11 位表示指数,52 位表示尾数,其中指数域称为阶码。IEEE 754浮点数的格式如下图所示。

注意,IEE754规定浮点数阶码E采用”指数e的移码-1“来表示,请记住这一点。为什么指数移码要减去1,这是IEEE754对阶码的特殊要求,以满足特殊情况,比如对正无穷的表示。

2.浮点数的规格化

若不对浮点数的表示作出明确的规定,同一个浮点数的表示就不是唯一的。例如(1.75)10(1.75)10(1.75)_{10}可以表示成1.11×201.11×201.11\times 2^0,0.111×210.111×210.111\times2^1,0.0111×220.0111×220.0111\times2^2等多种形式。当尾数不为0时,尾数域的最高有效位为1,这称为浮点数的规格化。否则,以修改阶码同时左右移动小数点位置的办法,使其成为规格化数的形式。

2.1单精度浮点数真值

IEEE754标准中,一个规格化32位的浮点数x的真值表示为:

x=(−1)S×(1.M)×2ex=(−1)S×(1.M)×2e

x=(-1)^S\times(1.M)\times2^e

e=E−127e=E−127

e=E-127 其中尾数域值是1.M。因为规格化的浮点数的尾数域最左位总是1,故这一位不予存储,而认为隐藏在小数点的左边。

在计算指数e时,对阶码E的计算采用源码的计算方式,因此32位浮点数的8bits的阶码E的取值范围是0到255。其中当E为全0或者全1时,是IEEE754规定的特殊情况,下文会另外说明。

2.2双精度浮点数真值

64位的浮点数中符号为1位,阶码域为11位,尾数域为52位,指数偏移值是1023。因此规格化的64位浮点数x的真值是:

x=(−1)S×(1.M)×2ex=(−1)S×(1.M)×2e

x=(-1)^S\times(1.M)\times2^e

e=E−1023e=E−1023

e=E-1023

3.移码

移码(又叫增码)是对真值补码的符号位取反,一般用作浮点数的阶码,引入的目的是便于浮点数运算时的对阶操作。

对于定点整数,计算机一般采用补码的来存储。正整数的符号位为0,反码和补码等同于源码。

负整数符号位都固定为1,源码,反码和补码的表示都不相同,由原码表示法变成反码和补码有如下规则: (1)源码符号位为1不变,整数的每一位二进制数位求反得反码; (2)反码符号位为1不变,反码数值位最低位加1得补码。

比如,以一个字节8bits来表示-3,那么[−3]原=10000011[−3]原=10000011[-3]_原=10000011,[−3]反=11111100[−3]反=11111100[-3]_反=11111100,[−3]补=11111101[−3]补=11111101[-3]_补=11111101,那么-3的移码就是[−3]移=01111101[−3]移=01111101[-3]_移=01111101。

如何将移码转换为真值-3呢?先将移码转换为补码,再求值。

4.浮点数的具体表示

4.1十进制到机器码

(1)0.5 0.5=(0.1)20.5=(0.1)20.5=(0.1)_2,符号位S为0,指数为e=−1e=−1e=-1,规格化后尾数为1.0。

单精度浮点数尾数域共23位,右侧以0补全,尾数域:

M=[000 0000 0000 0000 0000 0000]2M=[000 0000 0000 0000 0000 0000]2

M=[000\ 0000\ 0000\ 0000\ 0000\ 0000]_2

阶码E:

E=[−1]移−1=[0111 1111]2−1=[0111 1110]2E=[−1]移−1=[0111 1111]2−1=[0111 1110]2

E=[-1]_移-1=[0111\ 1111]_2-1=[0111\ 1110]_2

对照单精度浮点数的存储格式,将符号位S,阶码E和尾数域M存放到指定位置,得0.5的机器码:

0.5=[0011 1111 0000 0000 0000 0000 0000 0000]20.5=[0011 1111 0000 0000 0000 0000 0000 0000]2

0.5=[0011\ 1111\ 0000\ 0000\ 0000\ 0000\ 0000\ 0000]_2。

十六进制表示为0.5=0x3f000000。

(2)1.5 1.5=[1.1]21.5=[1.1]21.5=[1.1]_2,符号位为0,指数e=0e=0e=0,规格化后尾数为1.1。

尾数域M右侧以0补全,得尾数域:

M=[100 0000 0000 0000 0000 0000]2M=[100 0000 0000 0000 0000 0000]2

M=[100\ 0000\ 0000\ 0000\ 0000\ 0000]_2

阶码E:

E=[0]移−1=[10000000]2−1=[01111111]2E=[0]移−1=[10000000]2−1=[01111111]2

E=[0]_移-1=[1000 0000]_2-1=[0111 1111]_2

得1.5的机器码:

1.5=[0011 1111 1100 0000 0000 0000 0000 0000]21.5=[0011 1111 1100 0000 0000 0000 0000 0000]2

1.5=[0011\ 1111\ 1100\ 0000\ 0000\ 0000\ 0000\ 0000]_2

十六进制表示为1.5=0x3fc00000。

(3)-12.5 −12.5=[−1100.1]2−12.5=[−1100.1]2-12.5=[-1100.1]_2,符号位S为1,指数e为3,规格化后尾数为1.1001,

尾数域M右侧以0补全,得尾数域:

M=[100 1000 0000 0000 0000 0000]2M=[100 1000 0000 0000 0000 0000]2

M=[100\ 1000\ 0000\ 0000\ 0000\ 0000]_2

阶码E:

E=[3]移−1=[1000 0011]2−1=[1000 0010]2E=[3]移−1=[1000 0011]2−1=[1000 0010]2

E=[3]_移-1=[1000\ 0011]_2-1=[1000\ 0010]_2

即-12.5的机器码:

−12.5=[1100 0001 0100 1000 0000 0000 0000 0000]2−12.5=[1100 0001 0100 1000 0000 0000 0000 0000]2

-12.5=[1100\ 0001\ 0100\ 1000\ 0000\ 0000\ 0000\ 0000]_2

十六进制表示为-12.5=0xc1480000。

用如下程序验证上面的推算,代码编译运行平台Win32+VC++ 2012:

#include <iostream>
using namespace std;

int main(){
    float a=0.5;
    float b=1.5;
    float c=-12.5;

    unsigned int* pa=NULL;
    pa=(unsigned int*)&a;
    unsigned int* pb=NULL;
    pb=(unsigned int*)&b;
    unsigned int* pc=NULL;
    pc=(unsigned int*)&c;

    cout<<hex<<"a=0x"<<*pa<<endl;
    cout<<hex<<"b=0x"<<*pb<<endl;
    cout<<hex<<"c=0x"<<*pc<<endl;

    return 0;
}

输出结果:

验证正确。

4.2机器码到十进制

(1)若浮点数x的IEEE754标准存储格式为0x41360000,那么其浮点数的十进制数值的推演过程如下:

0x41360000=[0 10000010 011 0110 0000 0000 0000 0000]0x41360000=[0 10000010 011 0110 0000 0000 0000 0000]

0x41360000=[0\ 10000010\ 011\ 0110\ 0000\ 0000\ 0000\ 0000]

根据该浮点数的机器码得到符号位S=0,指数e=阶码-127=1000 0010-127=130-127=3。

注意,根据阶码求指数时,可以像上面直接通过 “阶码-127”求得指数e,也可以将阶码+1=移码阶码+1=移码阶码+1=移码,再通过移码求其真值便是指数e。比如上面阶码10000010+1=10000011[移码]=>00000011[补]=3(指数e)10000010+1=10000011[移码]=>00000011[补]=3(指数e)10000010+1=10000011_{[移码]}=>00000011_{[补]}=3(指数e)。

包括尾数域最左边的隐藏位1,那么尾数1.M=1.011 0110 0000 0000 0000 0000=1.011011。

于是有:

x=(−1)S×1.M×2e=+(1.011011)×23=+1011.011=(11.375)10x=(−1)S×1.M×2e=+(1.011011)×23=+1011.011=(11.375)10

x=(-1)^S\times1.M\times2^e=+(1.011011)\times2^3=+1011.011=(11.375)_{10}

通过代码同样可以验证上面的推算:

#include <iostream>
using namespace std;

int main(){
    unsigned int hex=0x41360000;
    float* fp=(float*)&hex;
    cout<<"x="<<*fp<<endl;
    return 0;
}

输出结果:

验证正确。

5.浮点数的几种特殊情况

(1)0的表示 对于阶码为0或255的情况,IEEE754标准有特别的规定: 如果 阶码E=0并且尾数M是0,则这个数的真值为±0(正负号和数符位有关)。

因此+0的机器码为:0 00000000 000 0000 0000 0000 0000 0000。 -0的机器码为:1 00000000 000 0000 0000 0000 0000 0000。

需要注意一点,浮点数不能精确表示0,而是以很小的数来近似表示0。因为浮点数的真值等于(以32bits单精度浮点数为例):

x=(−1)S×(1.M)×2ex=(−1)S×(1.M)×2e

x=(-1)^S\times(1.M)\times2^e

e=E−127e=E−127

e=E-127 那么+0的机器码对应的真值为1.0×2−1271.0×2−1271.0\times2^{-127}。同理,-0机器码真值为−1.0×2−127−1.0×2−127-1.0\times2^{-127}。

(2)+∞+∞+\infty 和−∞−∞-\infty的表示 如果阶码E=255 并且尾数M全是0,则这个数的真值为±∞(同样和符号位有关)。因此+∞的机器码为:0 11111111 000 0000 0000 0000 0000 0000。-∞的机器吗为:1 11111111 000 0000 0000 0000 0000 0000。

(3)NaN(Not a Number) 如果 E = 255 并且 M 不是0,则这不是一个数(NaN)。

6.浮点数的精度和数值范围

6.1浮点数的数值范围

根据上面的探讨,浮点数可以表示-∞到+∞,这只是一种特殊情况,显然不是我们想要的数值范围。

以32位单精度浮点数为例,阶码E由8位表示,取值范围为0-255,去除0和255这两种特殊情况,那么指数e的取值范围就是1-127=-126到254-127=127。

(1)最大正数 因此单精度浮点数最大正数值的符号位S=0,阶码E=254,指数e=254-127=127,尾数M=111 1111 1111 1111 1111 1111,其机器码为:0 11111110 111 1111 1111 1111 1111 1111。

那么最大正数值:

PosMax=(−1)S×1.M×2e=+(1.11111111111111111111111)×2127≈3.402823e+38PosMax=(−1)S×1.M×2e=+(1.11111111111111111111111)×2127≈3.402823e+38

PosMax=(-1)^S\times1.M\times2^e=+(1.111 1111 1111 1111 1111 1111)\times2^{127}\approx3.402823e+38 这是一个很大的数。

(2)最小正数 最小正数符号位S=0,阶码E=1,指数e=1-127=-126,尾数M=0,其机器码为0 00000001 000 0000 0000 0000 0000 0000。

那么最小正数为:

PosMin=(−1)S×1.M×2e=+(1.0)×2−126≈1.175494e−38PosMin=(−1)S×1.M×2e=+(1.0)×2−126≈1.175494e−38

PosMin=(-1)^S\times1.M\times2^e=+(1.0)\times2^{-126} \approx1.175494e-38

这是一个相当小的数。几乎可以近似等于0。当阶码E=0,指数为-127时,IEEE754就是这么规定1.0×2−1271.0×2−1271.0\times2^{-127}近似为0的,事实上,它并不等于0。

(3)最大负数 最大负数符号位S=1,阶码E=1,指数e=1-127==-126,尾数M=0,机器码与最小正数的符号位相反,其他均相同,为:1 00000001 000 0000 0000 0000 0000 0000。

最大负数等于:

NegMax=(−1)S×1.M×2e=−(1.0)×2−126≈−1.175494e−38NegMax=(−1)S×1.M×2e=−(1.0)×2−126≈−1.175494e−38

NegMax=(-1)^S\times1.M\times2^e=-(1.0)\times2^{-126} \approx-1.175494e-38

(4)最小负数 符号位S=0,阶码E=254,指数e=254-127=127,尾数M=111 1111 1111 1111 1111 1111,其机器码为:1 11111110 111 1111 1111 1111 1111 1111。

计算得:

NegMin=(−1)S×1.M×2e=+(1.11111111111111111111111)×2127=−3.402823e+38NegMin=(−1)S×1.M×2e=+(1.11111111111111111111111)×2127=−3.402823e+38

NegMin=(-1)^S\times1.M\times2^e=+(1.111 1111 1111 1111 1111 1111)\times2^{127}=-3.402823e+38

6.2浮点数的精度

说道浮点数的精度,先给精度下一个定义。浮点数的精度是指浮点数的小数位所能表达的位数。

阶码的二进制位数决定浮点数的表示范围,尾数的二进制位数表示浮点数的精度。以32位浮点数为例,尾数域有23位。那么浮点数以二进制表示的话精度是23位,23位所能表示的最大数是223−1=8388607223−1=83886072^{23}-1=8388607,所以十进制的尾数部分最大数值是8388607,也就是说尾数数值超过这个值,float将无法精确表示,所以float最多能表示小数点后7位,但绝对能保证的为6位,也即float的十进制的精度为为6~7位。

64位双精度浮点数的尾数域52位,因252−1=4,503,599,627,370,495252−1=4,503,599,627,370,4952^{52}-1=4,503,599,627,370,495,所以双精度浮点数的十进制的精度最高为16位,绝对保证的为15位,所以double的十进制的精度为15~16位。。

7.小结

本文操之过急,但也花了将近一天的时间,难免出现编辑错误和不当说法,请网友批评指正。不明之处,欢迎留言交流。对浮点数的乘法、除法运算还未涉及,后续可能会去学习并记录学习所得,与大家分享。


参考文献

[1]移码.百度百科 [2]关于IEEE754标准浮点数阶码的移码.百度知道 [3]计算机组成原理第四版[M].白中英.科学出版社:P16-P30 [4]浮点数.维基百科

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我的博客

算法复杂度

算法复杂度 分为时间复杂度和空间复杂度。即算法在编写成可执行程序后,运行时所需要的资源,资源包括时间资源和内存资源。 时间复杂度 在计算机科学中,算法的时间复杂...

33260
来自专栏TensorFlow从0到N

讨厌算法的程序员 1 - 插入排序

什么是算法 在说插入排序之前,我们了解下《算法导论》对算法的从两种不同角度的定义。 一般性解释: 算法是定义良好的计算过程,它取一个或一组值作为输入,并产生...

34440
来自专栏IT可乐

深入理解计算机系统(2.7)------二进制小数和IEEE浮点标准

  整数的表示和运算我们已经讲完了,在实际应用中,整数能够解决我们大部分问题。但是某些需要精确表示的数,比如某件商品的价格,某两地之间的距离等等,我们如果用整数...

27480
来自专栏python读书笔记

《python算法教程》Day3 - 递归递归简介代码示例

这是《python算法教程》的第3篇读书笔记。由于之前看书的效率太低了,所以拖了一个多星期才写第三篇读书笔记。这次主要简单总结一下递归(recursion)。 ...

31280
来自专栏数据小魔方

让执着成为一种习惯——仿网易数独玫瑰气泡图

没有难学的技艺,只有不够辛勤的付出! 今天这篇文章推送仿的的是网易数独的一幅信息图,内容呈现的是全球各国人民对于养老所持的态度,数据来源于Pew Reserch...

42550
来自专栏杨建荣的学习笔记

Java随机数算法(一)(r11笔记第14天)

问:如何生成一个随机的字符串?答:让新手退出VIM 。 这可能也是随机字符的一种由来:) 我们今天要说的是随机数算法,这个我策划了好久,但是进展缓慢。...

48470
来自专栏java初学

top K 问题

489160
来自专栏技术之路

climits

<climits>头文件定义的符号常量 CHAR_MIN       char的最小值 SCHAR_MAX      signed char 最大值 SCHAR...

18780
来自专栏Leetcode名企之路

【Leetcode】60. 第k个排列

给定 n 的范围是 [1, 9]。 给定 k 的范围是[1, n!]。 示例 1:

33520
来自专栏漫漫全栈路

计算机组成原理-运算方法之数据格式

数据格式 先说下数据格式,在选择计算机数的表示方式时,需要考虑以下几个因数: 要表达的书的类型(小数,整数,实数,复数) 可能遇到的数值范围 数值精度 数据存储...

29460

扫码关注云+社区

领取腾讯云代金券