计算机中的正负数表示

计算机中的正负数表示

在32位系统中,int类型占4个字节,一共是32个2进制位,int类型的首位是符号位,0代表正数,1代表负数,int的最大值是0x7fffffff(即除了最高的1Bit其他31位都为1),而最小值是0x80000000(除了最高1bit,其他31位都为0)。但是我有几个问题,在这里记录解答一下

关于补码
计算机中的补码是为了方便负数方便运算而设置的,因为如果直接相加,结果是不正确的,正数的补码是其自身,而负数的补码是,符号位不变,取其它位的反码,然后+1。注意,负数实际上是按照补码存储在计算机中的
举两个例子:
-5 + 6
-5 = 1101 (第一位是符号位)
-5的补码为1010 + 1 = 1011
6 = 0110
二者相加1011 + 0110 = 10001 = 0001 = 1 = (-5)+ 6结果是正确的
再看一个例子
-6 + 5
-6 = 1110 补码为 1001+1 = 1010
5 = 0101
-6 + 5 = 1010 + 0101 = 1111
这里的1111,注意显示的是负数的补码,所以要得到正确值,需要转换为原码
1111 => 1110 =>取反后得到,1001 => -1

为什么正负数的绝对值大小不一样
首先我很奇怪就是,在计算机语言中,int类型的最大值是01111111…1111(31个1),这个很容易理解,因为0是符号位,所以int的最大值是2147483647,按照这个逻辑,那么负数的值应该为1111111…111(32个1),第一个1是符号位,那么int的最小值不应该是-2147483647么,而实际上int的最小值是-2147483648,这是为什么呢?

为了解答这个问题,首先回到上述概念,可以发现,由于32位的int前面有一位符号位,那么如果按照之前的逻辑,那么1000000…000(31个0)和000…000(32个0),也就是正0和负0,不是一样的么?根据我的理解,为了不让数据浪费,规定,负0作为int类型的最小值,也就是-1 * 2^31,所以int类型范围在- 2^312^31 - 1之间

Int类型的最大值加一后,会变为最小值
可以先看一个代码的例子:

int main()
{
	int a = 0x80000000;
	int b = 0x7fffffff;

	std::cout << "MinInteger: " << a << std::endl;
	std::cout << "MinInteger - 1: " << a - 1 << std::endl;

	std::cout << "MaxInteger: " << b <<std::endl;
	std::cout << "MaxInteger + 1: " << b + 1 <<std::endl;

	std::cin.get();

	return 0;
}

输出结果如下:
在这里插入图片描述
可以看到,最大值+1变为了最小值,最小值-1变为了最大值,根据上面讲的补码的知识,我们可以分析一下:
在计算机中,Max和Min对应的值分别为:

0111 1111 1111 1111 1111 1111 1111 1111 : Max Interger = 2^31 - 1
1000 0000 0000 0000 0000 0000 0000 0000:Min Interger = - 2^31

但是实际计算时是按照补码来的,所以其补码为:

0111 1111 1111 1111 1111 1111 1111 1111 : Max Interger = 2^31 - 1
1000 0000 0000 0000 0000 0000 0000 0000:Min Interger = - 2^31,我们发现最小值的补码与其原码相同

OK,那么我们开始计算,首先计算Max + 1:

0111 1111 1111 1111 1111 1111 1111 1111 // Max
0000 0000 0000 0000 0000 0000 0000 0001
---------------------------------------
1000 0000 0000 0000 0000 0000 0000 0000 

可以看到结果正好为最小值

再计算Min - 1

1000 0000 0000 0000 0000 0000 0000 0000 // Min
1111 1111 1111 1111 1111 1111 1111 1111 // -1的补码
---------------------------------------
0111 1111 1111 1111 1111 1111 1111 1111 // 最高位溢出

所以说,了解了底层补码是怎么设计的,上述问题自然就能够得到解释。

为什么这么设计
主要是基于以下几点:

  • 将所有的数用其补码表示,可以将加减法统一用加法来处理,简化计算。
  • 不同的位组成不同的排列组合,每一种组合都对应一个独一的数,最大化利用数据
  • 尽量减少数据溢界时,造成的数据间断的情况

更多细节可以参考:
https://www.zhihu.com/question/30395946/answer/696377317
https://www.youtube.com/watch?v=4qH4unVtJkE

注意一点

int a = 0x80000000;
cout << a << endl;// print -2147483648
cout << 0x80000000 << endl; // print 2147483648,注意这里会默认当作比int更大的类型

猜你喜欢

转载自blog.csdn.net/alexhu2010q/article/details/105618240