【c++】为什么int和float类型都占有4个字节,但是float类型表示范围却比int类型大好多?

版权声明:本文为博主原创文章,转载请标明出处 https://blog.csdn.net/C2681595858/article/details/84865920
  • 原因很简单,就是int类型是直接以二进制形式保存,而float类型是以指数形式保存。
  • 下面内容对于初学者来说如果继续看下去收获肯定会不小,但是也会有一定的挑战,还是希望你静下心,认真看下去。

一、背景

  • 这几天在写《算法设计与分析的作业》期间遇到了用int和float类型最大值,初始化变量的问题。查了一下,int可以用<limits.h>中的INT_MAX初始化,float可以用<float.h>中的FLT_MAX初始化,但是我就很难受,为什么都是基本类型的最大值,却要包含两个头文件,能不能偷懒,用int最大值直接去初始化float或者float最大值初始化int呢?按照以前的错误理解,int和float字节数相同,但是float有小数部分,所以它表示的范围更窄,我呢就写了一个程序看了下,这两个到底相差多大,偷懒的做法风险有多高,所以就写了下面的程序:
#include <iostream>
#include <limits.h>
#include <float.h>
using namespace std;
int main()
{
  cout<<INT_MAX<<endl;
  cout<<FLT_MAX<<endl;
  return 0;
}

结果如下:
在这里插入图片描述

  • 不试不知道,一试吓一跳啊,float的表示范围怎么会那么大,该不会他们使用的内存空间不一样吧:
#include <iostream>
#include <limits.h>
#include <float.h>
using namespace std;
int main()
{
  cout<<INT_MAX<<endl;
  cout<<FLT_MAX<<endl;
  cout<<sizeof(int)<<endl;
  cout<<sizeof(float)<<endl;
  return 0;
}
  • 结果:
    在这里插入图片描述
  • 果然是我太菜了,那就好好看下到底是怎么回事吧:

二、int类型分析

1、分析

  • int 用4字节保存,最多表示 2 4 8 = 2 32 2^{4*8} = 2^{32} , 有正数和负数,所以可表示 2 31 1 2^{31}-1 个正数,即其最大值为2147483647,和上面结果一致,最小值-2147483648, 最大值比最小值的绝对值小1, 这是因为0占据了正数的一个表示名额。
  • 具体如下:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
  • 总共32位,也就是说它的能存储的数字就是32位全0,到32位全1。如果这 样的话它就只能存储正数了,但是我们计算也要用到负数,这可不太妙啊。所以为了能够即能存储正数又能够存储负数,科学家在设计计算机的时候把这32位数据进行了重新解释,从全0开始,一直到, 除最前一位之外,其余位都为1,这些表示他们对应的正数。就是下面这种形式
    从全0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

到除第一位是0之外全都是1

0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

上面范围都表示正数。可以计算一下,上面这个数刚好就是对应的最大的int类型。

  • 剩下的都表示负数了,那么问题又来了,这些负数有分别是怎么表示的呢?
    我们接着上面的分析,最大的正数表示形式(就是上面写出来的除第一位是0之外全都是1的那个形式)在加一,就成了下面的样子:
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

这个数被计算机解释为最小的负数,就是-2147483648,然后依次类推,32的表示形式每加1, 它所表示的形式也就加1,一直到最大的负数-1,32位表示形式就成了全1

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

好了关于int存储大致解释清楚了,但是我掌握的这些到底对不对呢?下面我们使用汇编来看一下,到底是不是这样:

2、验证(汇编)

  • 这里用mips汇编来查看其存储方式, 把下面这段代码在汇编器Qtspim中运行(运行前请先删除汉字注释):
.data
int_1: .word 0                                   ;定义了一个4字节的变量int_1,并把它赋值为0
int_2: .word 2147483647		;定义了一个4字节的变量int_2,并把它赋值为2147483647
int_3: .word -1					;定义了一个4字节的变量int_3,并把它赋值为-1
int_4: .word -2147483648		;定义了一个4字节的变量int_4,并把它赋值为-2147483648
.text
main:
	 lw $t1,int_1			;把上面定义的变量int_1取值到寄存器t1中
	 lw $t2,int_2			;把上面定义的变量int_2取值到寄存器t2中
	 lw $t3,int_3
	 lw $t4,int_4
		  

查看寄存器情况如下:
在这里插入图片描述
和上面解释的情况相同。

三、float类型分析

1、分析

  • float同样也是4字节保存只不过他的保存形式式如下:
    在这里插入图片描述
  • 也就是说第一个位置是符号位,如果它为0表示正数,为1表示负数
  • 第2-9位共8个位是指数位,这里的指数位使用的是带偏阶的计数法1,就是说,这里存储的指数 = 真正指数+127,这里的127是它的偏阶。那么它最大能够存储多大的指数呢,8位最多能表示 2 8 1 2^{8}-1 个数字也就是255个数字,从全0(0)到全1(255)。但是IEEE754的指数全1是不能表示数字的;真正指数 = 这里存的指数-127,所以最大能够存储的指数是254-127 = 127,最小能够存储的指数是1-127 = -126,这里最小用了1减,而没有用0,是因为当指数为0,而尾数非0,这不是规格化数。所以最终指数部分能够表示的范围是-126 ~ 127
  • 在看尾数部分,IEEE754隐藏了一个前导1,也就是说,这里存储的尾数 = 真正尾数 - 1,所以我们在将存储的数据还原为原先的数据时一定要加上1.

2、验证(汇编)

.data
float_1: .float -0.5
float_2: .float 0.0
float_3: .float 3.1875
.text
main:
	 lw $t1,float_1
	 lw $t2,float_2
	 lw $t3,float_3
		

结果如下:
在这里插入图片描述
结果分析:

  • -0.5, 是负数所以第一位为1,0.5 = 0. 1 2 0.1_{2} = 1.0 2 ( 2 ) 1 1.0*2^{-1}_{(2)} . 所以它的指数部分应该是-1+127 = 126 = 0111111 0 2 01111110_2 ,尾数1.0,隐藏1,最终结果和上面的一致。
  • 0,符号位0和1都行,只要指数和尾数部分都为0即可。
  • 3.1875 化成二进制就是 11.001 1 2 11.0011_2 (十进制浮点数化为二进制),然后科学计数法表示为 1.1001 1 2 1.10011_2 * 2 1 2^{1} ,这样指数部分为1,1+127 = 128,华为二进制就是 1000000 0 2 10000000_2 尾数部分去掉隐藏的1,就是 1001 1 2 10011_2 ,上面显示时,t3要比t1少一位,这是因为t3是正数,显示时省略了符号位0。

  1. 具体可以看这本书计算机组成与设计这里面第三章有一部分讲的是浮点表示很有用。 ↩︎

猜你喜欢

转载自blog.csdn.net/C2681595858/article/details/84865920