通俗易懂的Integer类型的范围和溢出简析
1) int类型的取值范围
前置知识:
两位比特有几种组合形式?
00
01
10
11
四种。
它可以表示几个数?分别为几个?
4个,是0,1,2,3。
好,
众所周知,int类型是4个字节的变量类型,由于1个字节表示8个比特(bits),所以用二进制类型表示的一个int应该为:
00000000 00000000 00000000 00000000
一共32个比特.。
所以说,理论上,一共有(2的32次方)种组合形式。如果每种格式在逻辑上表示为1个不同的数,那么一个int可以表示(2的32次方)个数。
但实际上,常识上的int是包含正数和负数的,于是为了表示方便(反正2的32次已经很大了),将int类型从左往右数第一个0作为符号位,不作为值的计算范围,且规定一个int变量这一个位上,为0为正,为1为负。所以能够表示值的位数只有31位了。例子如下:
0 |0000000 00000000 00000000 00000111 说明它为正数
1 |0000000 00000000 00000000 00000111 说明它为负数
现在问题在于,有一个数它既不是正也不属于负,它是谁? 他是0。
那么0该怎么表示?它的首位为1还是0?
为了方便,我们就规定他为 00000000 00000000 00000000 00000000,更符合人的直觉。可是10000000 00000000 00000000 00000000该表示什么?听我慢慢道来。
我们常常在书上可以看到,int类型的取值范围是 -2的31次方~2的31次方减1
2的31次方减1我们很容易理解,因为它等于
01111111 11111111 11111111 11111111
但我们想要表示另一个极端情况,-2的31次方我们会用什么表示?
没错,就是 10000000 00000000 00000000 00000000,就是那种我们感觉也可以当0取的可能性,它有了新的职责,它用来表示一个独特的值。
好,这样我们就要进入到比较难的环节了,负数到底怎么表示?
这里要说到原码 反码 补码的概念。
原码是方便人去理解的,计算机运算和编程不会去用原码。
原码符合人的直觉认知,判断流程就是:
看第一位是0还是1决定它是不是负数 + 看后面二进制数值位的值决定它到底是几。
如:
0 |0000000 00000000 00000000 00000111 说明它为正7
1 |0000000 00000000 00000000 00000111 说明它为负7
但计算机中,数值的表示与存储用的是补码
大白话就是,程序中的int中的数值用的是补码,计算机不在乎什么原码
个人认为,反码补码就是为了方便计算机进行对负数的处理在研究出来的,可以更方便的表示负数的值,以及进行正负数的计算。
反码是:
负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
正数的反码和原码是一样的!规定就是这样的。
[+1] =
[00000000 00000000 00000000 00000001]原=
[00000000 00000000 00000000 00000001]反
[-1] = [10000000 00000000 00000000 0000001]原
= [11111111 11111111 11111111 11111110]反
负数的补码即在其反码的基础上+1,
正数的补码等于原码
[-1] =
[10000000 00000000 00000000 00000001]原=
[11111111 11111111 11111111 11111110]反=
[11111111 11111111 11111111 11111111]补,
负数在计算机中的表示是用补码来表示的。
注意!正数的三个码都一样,所以正数就是特例情况我感觉三个码压根不是给正数用的,计算机去理解正数非常的方便。
但负数就不一样了。所以要引入其他码。
PS:个人理解,数值位更倾向于表示相对的大小,配合符号位才能更好的展现一个int的值到底是多少。我感觉补码这个名字不太好,弄的它好像是配角一样,其实感觉补码才是三种码中最重要的。
你去用C语言编译器输出-1的值二进制就是11111111 11111111 11111111 11111111
肯定有人会想,为什么-1在计算机中的表示会那么麻烦?直接用
1 |0000000 00000000 00000000 00000001不好吗?
真的不好,请想想到底是-1更大还是-999999更大?是-1更大吧!由于数学逻辑上,负数的特殊性导致其绝对值越大,负数反而小,不然正负数计算会变成一团乱麻。为了能够更正确的理解,负1在计算机中表示为,即补码为
1 |1111111 11111111 11111111 11111111 其实-1才是负整数中最大的数哦!
1 |1111111 11111111 11111111 11111110 这是-2!
1 |0000000 00000000 00000000 00000001 这是-2的31次方减1!
依次类推,补码表示为10000000 00000000 00000000 00000000 的它作为-2的31次方,它才是负数里最小的数。它非常非常特别,感觉就是个特例,最高位既代表符号又代表数值,也就是说-2的31次方的原码和补码是相同的。
2) int的溢出
有下面一个int型变量,
01111111 11111111 11111111 11111111
没错,他是int里最大的正数,也是最脆弱的正数,它现在在溢出的边缘上
如果我们动一动它,加个1会怎么样(计算机:行吧)
符号位:?什么B动静? woc!
10000000 00000000 00000000 00000000 <–2的31次方 (这里是补码)
没错,你给一个正数加一,它突然就变成了负数。因为你动用了符号位,把符号位参与到了数值的变动,导致想要的数据出了问题!他没有变成+2的31次方!
再加个1怎么样?
10000000 00000000 00000000 00000001 <–2的31次方-1 (这里是补码,别老惦记那原码了) (其实它在变大了,毕竟加一了)
所以在面对数值变动,溢出一定要考虑,不然从负数突然到正数出的bug绝对够你喝一壶的。
第一次写博客,写的很粗糙,只是分享自己在学习中的一些想法,有错误指正和改进欢迎提出!