同样是占32个坑,凭啥你float就比int的范围更大?

原文出处:https://zhuanlan.zhihu.com/p/84453627?from_voters_page=true

ok,这里先说明一下,假设是在32位的机器上,int是32位。而float使用的是IEEE 754标准的单精度浮点数格式也是占用32位。

这时候float和int都是占用32位,占用同样的空间,但float范围是更大的,那我们为啥还要int呢?为啥不节省空间,只用float?我们来一探究竟!他们在计算机的大脑里是如何记忆的?

1、int对32个坑是如何使用的?

(实名吐槽知乎,居然没有表格。。。)

int类型的使用方法,大多学过计算机的,应该都是非常清楚的。二进制存储即可。

例1:请写出165(10进制)使用32位int型存储在计算机中的形式。

10进制转换为2进制,我个人喜欢先转为16进制,再写成2进制。

如下: [公式]

那么165在32位int型中是这样存储的(中间的0,我省略了):

165在计算机中的int存储

非常简单明了,好理解。把10进制转换2进制,直接存进去就ok,前面空位补0。

例2:请写出-165(10进制)使用32位int型存储在计算机中的形式。

这是一个负数,按照惯例int型首位为符号位。0表示在正数,1表示负数。

如下: [公式]

但在计算机中,负数存储的是补码,不是原码。

他们之间按照如下转换:

原码:1000 0000 1010 0101

反码:1111 1111 0101 1010 (除了符号位,其它取反)

补码:1111 1111 0101 1011 (在反码的基础上加1即可)

那么-165在32位int型中是这样存储的(中间的1,我省略了):

-165在计算机中的int存储

比正数复杂了一点,但是还是可以很容易算出来的。

问题来了?为啥负数要用补码?这不是挑事吗?原码不好吗?

原因之一在于,我们计算: [公式] ,在计算机中存储的是二进制,

如果使用原码进行计算,需要单独把符号位拿出来,再做减法运算,而把符号位区分出来是需要额外的硬件电路支撑的,这很不方便。

如果使用补码,如下所示(这里按照16位进行举例):

0000 0000 1010 0101 + 1111 1111 0101 1011=0000 0000 0000 0000;

使用补码参与运算后,无需再管符号位,可以让符号位直接参与运算。这就是使用补码的最大的好处。到这里大家有没有发现,int型的这种存储方案是没有考虑小数的,所以这是整型的。关于int型的存储,不再赘述,整体来说还是清晰明了的一种方案。


2、float对32个坑是如何使用的?

同样也是占用32个坑,float型的范围比int就大很多,而且还能表示小数,那么它到底是如何利用这32个坑的呢?

例3:请写出165.25(10进制)使用float型存储在计算机中的形式。

同样我们还是先转换为2进制:[公式] 1010 0101 . 0100

那么如何把上面的二进制小数存到32个坑里呢?

在填坑之前,我们先要规范二进制小数的表示形式,就和我们的科学计数法一样的道理。

(就像 [公式] 要写成 [公式] 这个样子,把所有的小数换成统一的格式)

IEEE754标准做了这样的规定:当尾数(小数)不为0时,尾数域的最高有效位为1,这称为浮点数的规格化。

例如: [公式]

规格化后的二进制小数,有了统一的规格,可以发现这样规格化之后,我们只需要存储一个尾数(即小数部分,整数部分恒为1)和指数部分。

IEEE754标准把float型的32个坑做了如下划分:

其中包含了1位符号位S,8位阶码E和23位尾数M。

[公式] ,要存储这个二进制小数;

首先符号位S,0表示正数,1表示负数。S=0;

再写出尾数M,即:M=0100 1010 1000 0000 0000 000;

然后算出阶码E,这里指数为:e=7=0000 0111,根据标准要求,E=e+127;

即:E=7+127=134=1000 0110;

那么把这三个数都填进坑里,就ok啦。

165.25在计算机中的float储存

这个计算过程稍微复杂点,但也还可以手算出来。

但是问题又来了:

1、浮点数的表示范围有多大?

2、为什么要用指数加上127,才是阶码E,而不是直接用指数存进去?

3、这个过程可以看出float有效位是尾数M加1也就是24位,阶码E只是我们规范科学计数法记录指数的,但int有效位是32位,float实际有效位比int少,那么在相互转换的过程中会出现什么问题?

我依次解释这3个问题:

1、浮点数的表示范围有多大?

float型定义的正无穷大

float型定义的负无穷小

可以得出当E= 1111 1111时,指数为255-127=128,但这并不是表示这个数是: [公式] ,在IEEE754把这种情况定义为无穷大,此时尾数必须全部为0,不能有其他值,否则就认为无效数字。

那么除了无穷大这个特殊的、人为定义的情况,float型能表示的最大的正整数是多少?最小的负整数是多少?当E= 1111 1111时,是IEEE754定义的特殊值即为无穷大,那么除此之外的最大值就是:E= 1111 1110,M也取最大值,即得到如下结果:

float型能存储的最大正整数

此时阶码E为254,指数即为e=254-127=127。这个数即为:

[公式] ;

对于尾数我们可以换一个写法:

1.1111 1111 1111 1111 1111 111=10-0.0000 0000 0000 0000 0000 001

这样尾数可以写成: [公式] ;

那么float能够表示的最大正整数就是: [公式] ,即为 [公式] 。

那么float能够表示的最大负整数就是: [公式] 。

2、为什么要用指数加上127,才是阶码E,而不是直接用指数存进去?

这就很容易说明了,我们举个例子:

例4:请写出0.75(10进制)使用float型存储在计算机中的形式。

写成二进制:[公式] 。再写成规划化的计数法: [公式] ;

发现问题了没有?这次的指数是个负数啦,而我们希望存储到机器里的阶码永远都是正值,因为我们不希望再浪费一个坑去保存阶码的正负号,于是乎,干脆把指数加上127,而指数能取到的最小值就是-127,这样就可以保证阶码E永远都是正数啦,我们就不用再考虑指数正负号的问题了。

E=-1+127=126=0111 1110;

M=1000 0000 0000 0000 0000 000;

0.75在计算机中的float储存

3、这个过程可以看出float有效位是尾数M加1也就是24位,阶码E只是我们用于规范科学计数法记录指数的,但int有效位是32位,float实际有效位比int少,那么在相互转换的过程中会出现什么问题?

通过问题1知道,float型的表示范围是比int大很多的,但有效位确实只有24位。既然float范围大,那么所有的int型都是可以转换为float型的,这是不会产生溢出报错的。但因为int型有效位是32位,是比float型的24位大的,是有可能发生舍入的,即当一个int型数字,转成float型后,可能就不再是原本数字了,损失了一定的精度。

例如2进制int型正数:0111 1111 1111 1111 1111 1111 1111 1111;

写成科学计数法即为: [公式]

小数点后面有30个1,但是我们知道float种尾数M只有23个坑。

则转化为float型后,阶码E=30+127=157=1001 1101

可以发现,我们对原int型中存储的数字只保留了小数点后23个1,而后面7个,直接忽视了,这就是发生了舍入。

如果既要范围大,还要保留精度,那就上双精度浮点型double,double型的存储规则和float型是十分类似的。double型有64个坑位,包括了1个符号位S,11个阶码位E和52个尾数位M。所以double的有效位有53位,可以完整保留int。

ok,同样是占32个坑,那凭啥你float就比int的范围更大?因为float型虽然范围大,但是精度不足啊!所以各有千秋哦。

posted on 2021-05-19 13:58  any91  阅读(1298)  评论(0编辑  收藏  举报