其他分享
首页 > 其他分享> > float类型的存储

float类型的存储

作者:互联网

float类型的存储

之前我们学过了Java的四种基本整数类型:

其中一个字节是8位,所以能表示的个数就是28*x个(其中x表示字节数)

因为有正数和负数,所以就是28*x-1到​28*x-1​-1个,正数为什么要减一呢,因为有个0算正整数。

可能看起来不太简洁但意思就是这个意思

而浮点数分别为float(4字节)和double(8字节)

也就是32位和64位

这在计算机中要怎么表示呢

无论是单精度还是双精度在存储中都分为三个部分:

首先我们知道常用科学计数法是将所有的数字转换成(±)a.b x 10c ​的形式,其中a的范围是1到9共9个整数,b是小数点后的所有数字,c是10的指数。

而计算机中存储的都是二进制数据,所以float存储的数字都要先转化成(±)a.bx2c,由于二进制中最大的数字就是1,所以表示法可以写成(±)1.b x 2c的形式,float要想存储小数就只需要存储(±),b和c就可以了。

float的存储正是将4字节32位划分为了3部分来分别存储正负号,小数部分和指数部分的:

Sign(1位):用来表示浮点数是正数还是负数,0表示正数,1表示负数。

Exponent(8位):指数部分。即上文提到数字c,但是这里不是直接存储c,为了同时表示正负指数以及他们的大小顺序,这里实际存储的是c+127

Mantissa(23位):尾数部分。也就是上文中提到的数字b。

三部分在内存中的分布如下,用首字母代替类型

SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMM
0 1 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1

float存储示例

以数字6.5为例,看一下这个数字是怎么存储在float变量中的:

先来看整数部分,模2求余可以得到二进制表示为110。

再来看小数部分,乘2取整可以得到二进制表示为.1(如果你不知道怎样求小数的二进制,请主动搜索一下)。

拼接在一起得到110.1然后写成类似于科学计数法的样子,得到1.101 x ​ 22

从上面的公式中可以知道符号为正,尾数是101,指数是2。

符号为正,那么第一位填0,指数是2,加上偏移量127等于129,二进制表示为10000001,填到2-9位,剩下的尾数101填到尾数位上即可

SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMM
0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

内存中二进制数01000000 11010000 00000000 00000000表示的就是浮点数6.5

float范围

明白了上面的原理就可求float类型的范围了,找到所能表示的最大值,然后将符号为置为1变成负数就是最小值,要想表示的值最大肯定是尾数最大并且指数最大,那么可以得到尾数为 0.1111 1111 1111 1111 1111 111,指数为 1111 1111,但是指数全为1时有其特殊用途,所以指数最大为 11111110,指数减去127得到127,所以最大的数字就是1.1111111 1111111 11111111 x 2127​,这个值为 340282346638528859811704183484516925440,通常表示成3.4028235E38 ​,那么float的范围就出来了:

[-3.4028235E38, 3.4028235E38]

float精度

float 类型的数据精度取决于尾数,相信大家都知道这一点,但是精度怎么算我也是迷糊了好久,最近在不断尝试的过程中渐渐的明白了,首先是在不考虑指数的情况下23位尾数能表示的范围是[0, ​223− 1],实际上尾数位前面还隐含了一个"1",所以应该是一共24位数字,所能表示的范围是[0, ​224​-1](因为隐含位默认是"1",所以表示的数最小是1不是0,但是先不考虑0,后面会特殊介绍,这里只按一般值计算),看到这里我们知道这24位能表示的最大数字为 ​224​-1,换算成10进制就是16777215,那么[0, 16777215]都是能精确表示的,因为他们都能写成1.b x​2c的形式,只要配合调整指数c就可以了。

16777215 这个数字可以写成1.1111111 11111111 1111111 *​ 223,所以这个数可以精确表示,然后考虑更大的数16777216,因为正好是2的整数次幂,可以表示1.0000000 00000000 00000000 *​ 224,所以这个数也可以精确表示,在考虑更大的数字16777217,这个数字如果写成上面的表示方法应该是 1.0000000 00000000 00000000 1 *​ 224,但是这时你会发现,小数点后尾数位已经是24位了,23位的存储空间已经无法精确存储,这时浮点数的精度问题也就是出现了。

看到这里发现 16777216 貌似是一个边界,超过这个数的数字开始不能精确表示了,那是不是所有大于16777216的数字都不能精确表示了呢?其实不是的,比如数字 33554432 就可以就可以精确表示成1.0000000 00000000 00000000 *​ 225,说道这里结合上面提到的float的内存表示方式,我们可以得出大于 16777216 的数字(不超上限),只要可以表示成小于24个2的n次幂相加,并且每个n之间的差值小于24就能够精确表示。换句话来说所有大于 16777216 的合理数字,都是[0, 16777215]范围内的精确数字通过乘以​得到的,同理所有小于1的正数,也都是 [0, 16777215] 范围内的精确数字通过乘以​得到的,只不过n取负数就可以了。

16777216 已经被证实是一个边界,小于这个数的整数都可以精确表示,表示成科学技术法就是1.6777216 *107 ​,从这里可以看出一共8位有效数字,由于最高位最大为1不能保证所有情况,所以最少能保证7位有效数字是准确的,这也就是常说float类型数据的精度。

float小数

从上面的分析我们已经知道,float可表示超过16777216范围的数字是跳跃的,同时float所能表示的小数也都是跳跃的,这些小数也必须能写成2的n次幂相加才可以,比如0.5、0.25、0.125…以及这些数字的和,像5.2这样的数字使用float类型是没办法精确存储的,5.2的二进制表示为101.0011001100110011001100110011……最后的0011无限循环下去,但是float最多能存储23位尾数,那么计算机存储的5.2应该是101.001100110011001100110,也就是数字 5.19999980926513671875,计算机使用这个最接近5.2的数来表示5.2。关于小数的精度与刚才的分析是一致的,当第8位有效数字发生变化时,float可能已经无法察觉到这种变化了。

float特殊值

我们知道float存储浮点数的形式是(±)1.b x 2c ​,因为尾数位前面一直是个1,所以无论b和c取什么样的值,都无法得到0,所以在float的表示方法中有一些特殊的约定,用来表示0已经其他的情况。

float的内存表示指数位数有8位,范围是[0, 255],考虑偏移量实际的指数范围是[-127,128],但实际情况下指数位表示一般数字时不允许同时取0或者同时取1,也就是指数位的实际范围是[-126,127],而指数取-127和128时有其特殊含义,具体看下面表格:

符号位指数位尾数位数值含义
0 全为0 全为0 +0 正数0
1 全为0 全为0 -0 负数0
0 全为0 任意取值f 0.f∗2-126 非标准值,尾数前改为0,提高了精度
1 全为0 任意取值f 0.f∗2-126 非标准值,尾数前改为0,提高了精度
0 全为1 全为0 +Infinity 正无穷大
1 全为1 全为0 -Infinity 负无穷大
0/1 全为1 不全为0 NaN 非数字,用来表示一些特殊情况

总结

  1. float的精度是保证至少7位有效数字是准确的

  2. float的取值范围[-3.4028235E38, 3.4028235E38],精确范围是[-340282346638528859811704183484516925440, 340282346638528859811704183484516925440]

  3. 一个简单的测试float精度方法,C++代码中将数字赋值给float变量,如果给出警告warning C4305: “=”: 从“int”到“float”截断,则超出了float的精度范围,在我的测试中赋值为16777216及以下整数没有警告,赋值为16777217时给出了警告。

 

标签:表示,全为,存储,数字,尾数,float,类型
来源: https://www.cnblogs.com/recorderM/p/15489651.html