上节咱们提到正整数相乘的结果竟然出现了负数,要理解这个行为,咱们须要看下整数在计算机内部的二进制表示。html
十进制swift
要理解整数的二进制,咱们先来看下熟悉的十进制。十进制是如此的熟悉,咱们可能已忽略了它的含义。好比123,咱们不假思索就知道它的值是多少。spa
但其实123表示的1*(10^2) + 2*(10^1) + 3*(10^0),(10^2表示10的二次方),它表示的是各个位置数字含义之和,每一个位置的数字含义与位置有关,从右向左,第一位乘以10的0次方, 即1,第二位乘以10的1次方,即10,第三位乘以10的2次方,即100,依次类推。code
换句话说,每一个位置都有一个位权,从右到左,第一位为1,而后依次乘以10,即第二位为10,第三位为100,依次类推。htm
正整数的二进制表示blog
正整数的二进制表示与此相似, 只是在十进制中,每一个位置能够有10个数字,从0到9,但在二进制中,每一个位置只能是0或1。位权的概念是相似的,从右到左,第一位为1,而后依次乘以2,即第二位为2,第三位为4,依次类推。get
看一些数字的例子吧:table
二进制 | 十进制 |
10 | 2 |
11 | 3 |
111 | 7 |
1010 | 10 |
负整数的二进制表示class
十进制的负数表示就是在前面加一个负数符号-,例如-123。但二进制如何表示负数呢?基础
其实概念是相似的,二进制使用最高位表示符号位,用1表示负数,用0表示正数。
但哪一个是最高位呢?整数有四种类型,byte/short/int/long,分别占1/2/4/8个字节,即分别占8/16/32/64位,每种类型的符号位都是其最左边的一位。
为方便举例,下面假定类型是byte,即从右到左的第8位表示符号位。
但负数表示不是简单的将最高位变为1,好比说:
和咱们的直觉正好相反,这是什么表示法?这种表示法称为补码表示法,而符合咱们直觉的表示称为原码表示法,补码表示就是在原码表示的基础上取反而后加1。取反就是将0变为1,1变为0。
负数的二进制表示就是对应的正数的补码表示,好比说:
给定一个负数二进制表示,要想知道它的十进制值,能够采用相同的补码运算。好比:10010010,首先取反,变为01101101,而后加1,结果为01101110,它的十进制值为110,因此原值就是-110。直觉上,应该是先减1,而后再取反,但计算机只能作加法,而补码的一个良好特性就是,对负数的补码表示作补码运算就能够获得其对应整数的原码,正如十进制运算中负负得正同样。
byte类型,正数最大表示是01111111,即127,负数最小表示(绝对值最大)是10000000,即-128,表示范围就是 -128到127。其余类型的整数也相似,负数能多表示一个数。
负整数为何采用补码呢?
负整数为何要采用这种奇怪的表示形式呢?缘由是:只有这种形式,计算机才能实现正确的加减法。
计算机其实只能作加法,1-1实际上是1+(-1)。若是用原码表示,计算结果是不对的。好比说:
1 -> 00000001
-1 -> 10000001
+ ------------------
-2 -> 10000010
用符合直觉的原码表示,1-1的结果是-2。
若是是补码表示:
1 -> 00000001
-1 -> 11111111
+ ------------------
0 -> 00000000
结果是正确的。
再好比,5-3:
5 -> 00000101
-3 -> 11111101
+ ------------------
2 -> 00000010
结果也是正确的。
就是这样的,看上去可能比较奇怪和难以理解,但这种表示实际上是很是严谨和正确的,是否是很奇妙?
理解了二进制加减法,咱们就能理解为何正数的运算结果可能出现负数了。当计算结果超出表示范围的时候,最高位每每是1,而后就会被看作负数。好比说,127+1:
127 -> 01111111
1 -> 00000001
+ ------------------
-128 -> 10000000
计算结果超出了byte的表示范围,会被看作-128。
十六进制
二进制写起来太长,为了简化写法,能够将四个二进制位简化为一个0到15的数,10到15用字符A到F表示,这种表示方法称为16进制,以下所示:
2进制 | 10进制 | 16进制 |
1010 | 10 | A |
1011 | 11 | B |
1100 | 12 | C |
1101 | 13 | D |
1110 | 14 | E |
1111 | 15 | F |
能够用16进制直接写常量数字,在数字前面加0x便可。好比10进制的123,用16进制表示是0x7B,即123 = 7*16+11。给整数赋值或者进行运算的时候,均可以直接使用16进制,好比:
int a = 0x7B;
Java中不支持直接写二进制常量,好比,想写二进制形式的11001,Java中不能直接写,能够在前面补0,补足8位,为00011001,而后用16进制表示,即 0x19。
查看整数的二进制和十六进制表示
在Java中,能够方便的使用Integer和Long的方法查看整数的二进制和十六进制表示,例如:
int a = 25; System.out.println(Integer.toBinaryString(a)); //二进制 System.out.println(Integer.toHexString(a)); //十六进制 System.out.println(Long.toBinaryString(a)); //二进制 System.out.println(Long.toHexString(a)); //十六进制
位运算
位运算是将数据看作二进制,进行位级别的操做,Java不能单独表示一个位,可是能够用byte表示8位,能够用16进制写二进制常量。好比: 0010表示成16进制是 0x2, 110110表示成16进制是 0x36。
位运算有移位运算和逻辑运算。
移位有:
例如:
int a = 4; // 100 a = a >> 2; // 001,等于1 a = a << 3 // 1000,变为8
逻辑运算有:
大部分都比较简单,就不详细说了。具体形式,例如:
int a = ...; a = a & 0x1 // 返回0或1,就是a最右边一位的值。 a = a | 0x1 //无论a原来最右边一位是什么,都将设为1
小结
本节咱们讨论了整数的二进制表示,须要注意的就是负数的二进制表示,以及计算机进行二进制加减操做的过程,从而咱们就能理解为何有的时候正整数计算会出现负数。
咱们一样讨论了整数的位运算,须要注意的就是无符号右移和有符号右移的区别。
理解了整数,那小数呢?