进制
# 关于进制
所有数字在计算机底层都以二进制形式存在。二进制形式就是01010101……
对于整数,有四种表示方式:
- 二进制(binary)
0,1,满2进1,以0b
或0B
开头
- 十进制(decimal)
0-9,满10进1。在生活中,我们用的是十进制。
- 八进制(octal)
0-7,满8进1,以数字0开头表示。
- 十六进制(hex)
0-A及A-F,满16进1,以0x
或0X
开头表示。此处的A-F不区分大小写。如:0x21AF + 1 = 0X21B0
# 进制关系表
十进制 | 十六进制 | 八进制 | 二进制 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
2 | 2 | 2 | 10 |
3 | 3 | 3 | 11 |
4 | 4 | 4 | 100 |
5 | 5 | 5 | 101 |
6 | 6 | 6 | 110 |
7 | 7 | 7 | 111 |
8 | 8 | 8 | 1000 |
9 | 9 | 11 | 1001 |
10 | A | 12 | 1010 |
11 | B | 13 | 1011 |
12 | C | 14 | 1100 |
13 | D | 15 | 1101 |
14 | E | 16 | 1110 |
15 | F | 17 | 1111 |
16 | 10 | 20 | 10000 |
17 | 11 | 21 | 10001 |
# 练习
// - 二进制(binary) 0,1,满2进1,以`0b`或`0B`开头
int num1 = 0b110; // 6
// 十进制(decimal)
int num2 = 110; // 110
// - 八进制(octal) 0-7,满8进1,以数字0开头表示。
int num3 = 0127; // 87
// - 十六进制(hex) 0-A及A-F,满16进1,以`0x`或`0X`开头表示。此处的A-F不区分大小写。
int num4 = 0x110A; // 4362
2
3
4
5
6
7
8
9
10
11
发现输出结果都是以十进制形式输出。
# 二进制
- Java整数常量默认是
int
类型,当用二进制定义整数时,其第32
位是符号位;当是long
类型时,二进制默认占64
位,第64
位是符号位 - 二进制的整数有如下三种形式:
- 原码:直接将一个数值换成二进制数。最高位是符号位。
- 负数的反码:是对原码按位取反,只是最高位(符号位)确定为1.
- 负数的补码:其反码加1。
- 计算机以二进制补码的形式保存所有的整数。
- 正数的原码,反码,补码都相同
- 负数的补码是其反码+1
# 二进制转换十进制
# 1110
比如存在一个二进制数字:1110,转换为十进制数值是多少?
- 第一种方法:
根据进制表关系,或者去推算:
二进制 | 十进制 |
---|---|
10 | 2 |
100 | 4 |
1000 | 8 |
结果是14,那么有没有更简单的方法
- 第二种方法:
有,假设下面是内存空间
0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
---|---|---|---|---|---|---|---|
符号位 | 0*2^6 | 0**2^5 | 0*2^4 | 1*2^3 | 1*2^2 | 1*2^1 | 0*2^0 |
相加起来也是14。但是第二种方法只适用于原码。
符号位0表示整数,1表示负数。
# 1101110
比如存在一个二进制数:1101110,如何计算出十进制的实际值?
参考上个案例的第二种方法:
0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
---|---|---|---|---|---|---|---|
符号位 | 1*2^6 | 1*2^5 | 0*2^4 | 1*2^3 | 1*2^2 | 1*2^1 | 0*2^0 |
计算出结果是110。
# 10001110
那如果二进制数:10001110,十进制的实际值又是多少?
除了符号位,和上面二进制数1110(十进制14),其他都一样
- 原码:
1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
---|---|---|---|---|---|---|---|
符号位 | 0*2^6 | 0*2^5 | 0*2^4 | 1*2^3 | 1*2^2 | 1*2^1 | 0*2^0 |
那么已经算出结果是14,因为符号位是1,所以是附属,那么最终结果是-14
。
# 10111011
如果存在二进制的补码是10111011,那么它的十进制实际值是多少?
二进制的整数有如下三种形式:
- 原码:直接将一个数值换成二进制数。最高位是符号位。
- 负数的反码:是对原码按位取反,只是最高位(符号位)确定为1.
- 负数的补码:其反码加1。
计算机以二进制补码的形式保存所有的整数。
- 正数的原码,反码,补码都相同
- 负数的补码是其反码+1
既然题目已知了补码,那么推算原码后再换算成二进制就可以了。
- 补码
1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
---|---|---|---|---|---|---|---|
- 反码:负数的补码是其反码+1,那么反码就是补码-1
1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 |
---|---|---|---|---|---|---|---|
- 原码:反码是原码的取反,除了符号位。
1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 |
---|---|---|---|---|---|---|---|
1*2^6=64 | 1*2^2=4 | 1*2^0=1 |
那么合计结果是69,符号位是1,所以是-69。通过计算机计算-69的补码也是10111011。
# 填坑
在精度损失案例2 (opens new window)中,有一个小段代码
int i2 = 128;
byte b = (byte)i2;// -128
2
为什么是-128呢?来分析一下。
Java整数常量默认是int类型,当用二进制定义整数时,其第32位是符号位;
那么整数128的二进制数是:前面24个0 连上,1000 000。
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
---|---|---|---|---|---|---|---|
那么当强转为byte时,占用8个字节空间,那么前面24个0也砍掉了,只剩下1000 0000
在二进制数中,首位是符号位,表示为负数。
那么此时变量也变成了负数,也就-128。
# 进制之间的转化
二进制以上 转为 二进制的方法是除2取余的逆。具体怎么实现等下看例子。
进制的基本转换:
- 十进制 二进制互转
- 二进制转成十进制:乘以2的幂数
- 十进制转成二进制:除以2的余数
- 二进制 八进制互转
- 二进制 十六进制互转
- 十进制 八进制互转
- 十进制 十六进制互转
小技巧:多个进制转换,优先转换成二进制再转换成最终想要的,计算结果会快很多。
# 十进制转二进制
十进制数:13,求二进制数值是多少?
比如简单点的数字,自己能口算,8 + 4 + 1 = 2^3 + 2^2 + 2^0。
实际上:十进制转二进制的方法:除2取余的逆
13/2 = 6 余 1
6/2 = 3 余 0
3/2 = 1 余 1
1/2 = 0 余 1
2
3
4
后面怎么算都是0了,补0到8位数,按反方向生成二进制数。
所以十进制数13的二进制是00001101
,就写1101
都行了。
# 二进制 转 八进制
二进制 转 八进制的办法是,每3个数转换一次八进制,2^3=8,二的三次幂刚好等于八,每3个数,最大数就是8。
比如:
二进制数1110 1001,转换为八进制数,值是多少?
0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 |
---|---|---|---|---|---|---|---|---|
先从最后面的数字看,可以把二进制数分为3个一体来转换:
// 001
1*2^0 + 0*2^1 + 0*2^2 = 1
//101
1*2^0 + 0*2^1 + 1*2^2 = 5
//011 二进制数只有8位,但是前面补0不影响正数值。
1*2^0 + 1*2^1 + 0*2^2 = 3
2
3
4
5
6
7
8
八进制:0-7,满8进1,以数字0开头表示。
得出结果是0351
同理
# 二进制 转 十六进制
2^4=16,每4个数字看作一体。
比如:用回刚才的二进制数:1110 1001转换为 十六进制数值是多少?
1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 |
---|---|---|---|---|---|---|---|
//1001
1*2^0 + 0*2^1 + 0*2^2 + 1*2^3 = 1 + 0 + 0 + 8 = 9
//1110
0*2^0 + 1*2^1 + 1*2^2 + 1*2^3 = 0 + 2 + 4 + 8 = 14 // 在十六进制中14就是E
2
3
4
5
16进制:0-A及A-F,满16进1,以
0x
或0X
开头表示。此处的A-F不区分大小写。
得出结果是0XE9
# 八进制 转二进制
方法:除2取余的逆
同理,因为2^3=8
,每个数字拆成3个数字。
比如八进制数为:0357,转为二进制数值是多少?
//7
7/2 = 3 余 1
3/2 = 1 余 1
1/2 = 0 余 1
7 => 111
//5
5/2 = 2 余 1
2/2 = 1 余 0
1/2 = 0 余 1
5 => 101
//3
3/2 = 1 余 1
1/2 = 0 余 1
3 => 11
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
所以是1110 1111
。
# 十六进制 转二进制
方法:除2取余的逆
同理,因为2^4=16
,每个数字拆分4个数字
比如十六进制数为:0x3AF,转为二进制数值是多少?
//F 推算16进制>>A:10,B:11,C:12,D:13,E:14,那么F就是15,既然是最后一个数字其实全是1也是可以的。
15/2 = 7 余 1
7/2 = 3 余 1
3/2 = 1 余 1
1/2 = 0 余 1
F => 1111
//A=10
10/2 = 5 余 0
5/2 = 2 余 1
2/2 = 1 余 0
1/2 = 0 余 1
A => 1010
//3
3/2 = 1 余 1
1/2 = 0 余 1
3 => 11
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
所以结果是11 1010 1111
。
# Java 二进制API
在将来的java开发中如果需要用到二进制,可以找到Integer.class中的方法,使用API直接转换
- 二进制:toBinaryString
- 八进制:toOctalString
- 十六进制:toHexString
这里就不做展开了。
至此,已经掌握了各种进制的转换,以及弄清楚原码,反码,补码的概念。