float类型的存储

float类型的存储

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

  • byte(1字节)

  • short(2字节)

  • int(4字节)

  • long(8字节)

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

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

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

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

也就是32位和64位

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

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

  • 符号位(Sign) :0代表正,1代表为负

  • 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储

  • 尾数部分(Mantissa):尾数部分

  首先我们知道常用科学计数法是将所有的数字转换成(±)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

小数转换二进制方法:乘2取整法,即将小数部分乘以2,然后取整数部分,剩下的小数部分继续乘以2,然后取整数部分,剩下的小数部分又乘以2,一直取到小数部分为零为止。如果永远不能为零,就同十进制数的四舍五入一样,按照要求保留多少位小数时,就根据后面一位是0还是1,取舍,如果是零,舍掉,如果是1,向入一位。换句话说就是0舍1入。读数要从前面的整数读到后面的整数。

例:将0.375换算为二进制
得出结果:将0.375换算为二进制(0.011)2
分析:第一步,将0.375乘以2,得0.75,则整数部分为0,小数部分为0.75;
第二步, 将小数部分0.75乘以2,得1.5,则整数部分为1,小数部分为0.5;
第三步, 将小数部分0.5乘以2,得1.0,则整数部分为1,小数部分为0.0;
第四步,读数,从第一位读起,读出每一次的整数部分,读到最后一位,即为0.011。)

拼接在一起得到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时给出了警告。

 

PS:引用了一些csdn的,觉得有用就点个赞吧!

 

posted @ 2021-10-31 15:17  紫英626  阅读(783)  评论(0编辑  收藏  举报

紫英