Java 中的二进制
二进制是 计算机技术中广泛采用的一种数制。二进制数据是用 0 和 1 两个数码来表示的数。
它的基数为 2,进位规则是 “逢二进一”,借位规则是 “借一当二”
1. 正整数的二进制表示法
十进制 | 二进制 | 十进制 | 二进制 | 十进制 | 二进制 | 十进制 | 二进制 |
---|---|---|---|---|---|---|---|
0 | 0 | 3 | 11 | 6 | 110 | 9 | 1001 |
1 | 1 | 4 | 100 | 7 | 111 | 10 | 1010 |
2 | 10 | 5 | 101 | 8 | 1000 | 11 | 1011 |
1.1 十进制转换成二进制
示例:将 46 转换为二进制
从下往上 46 转换为 二进制: 101110
46 / 2 = 23;// 余 0
23 / 2 = 11;// 余 1
11 / 2 = 5; // 余 1
5 / 2 = 2; // 余 1
2 / 2 = 1; // 余 0
1 / 2 = 0; // 余 1
1.2 二进制转换成十进制
示例: 将 10110 转化为 十进制数
1 0 1 1 1 0
5 4 3 2 1 0 // 每个位上的值代表 2 的 n 次方
32 16 8 4 2 1 // 每个位上的 1 代表的十进制的大小
32+0 +8+ 4+ 2+ 0 = 46
2. 负整数的二进制表示法
内存中一个字节占 8 位,第一位表示符号位,0 表示正数,1 表示负数
符号位( 0 表示正数,1表示负数) 数字位 数字位 数字位 数字位 数字位 数字位 数字位 0 0 1 0 1 1 1 0
2.1 十进制转换为二进制
示例: 将 -46 转换为二进制数
将 46 转换为二进制数 --> 00101110
将这个二进制数按位去反 --> 11010001
最后对二进制数 +1 --> 11010010
2.2 二进制转换为十进制
将 8 位的二进制数字 11010010 转换成十进制
- 根据符号位判断 正负
- 正数直接按照上面的方法处理
- 负数
- 先 -1 --> 11010001
- 取反 --> 00101110
- 再按照正数的方式转换为十进制 46
- 加上符号得到最终结果: -46
3. 其他进制
十进制 | 二进制 | 八进制 | 十六进制 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
2 | 10 | 2 | 2 |
3 | 11 | 3 | 3 |
4 | 100 | 4 | 4 |
5 | 101 | 5 | 5 |
6 | 110 | 6 | 6 |
7 | 111 | 7 | 7 |
8 | 1000 | 10 | 8 |
9 | 1001 | 11 | 9 |
10 | 1010 | 12 | A |
11 | 1011 | 13 | B |
12 | 1100 | 14 | C |
13 | 1101 | 15 | D |
14 | 1110 | 16 | E |
16 | 10000 | 20 | 10 |
17 | 10001 | 21 | 11 |
18 | 10010 | 22 | 12 |
19 | 10011 | 23 | 13 |
20 | 10100 | 24 | 14 |
3.1 二进制转换成八进制
- 将二进制转换为 八进制, 2 的 3 次方等于 8,所以可以将二进制数的每 3 位转换位 8 进制数的 1 位。反之亦然。
- 如下图 01010111 转换为 八进制为 127
3.2 二进制转换成十六进制
将二进制转换成为 十六进制,由于 2 的 4 次方等于 16,所以可以将二进制的每 4 位 转换成 16 进制数的 1 位。反之亦然。
下图 将 01010111 转换成为 十六进制为 57
4. Java 中的多种进制
4.1 多种进制的表示
-
十进制
int a = 10;
-
二进制
Java 中 0b 或者 0B 开头表示 二进制数
int b = 0b0010; int c = 0B0010;
-
八进制
Java 中使用 0 开头表示八进制数
int d = 010;
-
十六进制
Java 中使用 0x 或 0X 开头表示 十六进制数
int e = 0x4A; int f = 0X4A;
4.2 多种进制的转换
-
整数类型十进制数 转换为二进制字符串
String binaryNumber = Integer.toBinaryString(100);
-
整数类型十进制数 转换为八进制字符串
String octalNumber = Integer.toOctalString(100);
-
整数类型十进制数 转换为十六进制字符串
String hexadecimalNumber = Integer.toHexString(100);
-
将字符串按照指定的进制进行解析
- 使用
Integer.valueOf()
方法解析字符串,默认按照 十进制解析,也可以自定义解析方式。 - 如果数据不符合解析的进制,会发生
NumberFormatException
。
String val = "31"; Integer.valueOf(val,8);// 按照 8 进制进行解析:25 Integer.valueOf(val,16);// 按照 16 进制进行解析:49
- 使用
4.3 二进制和 Java 中的数据类型
-
byte
-
Java 中 byte 占 8 位,8 位的二进制数最大为 01111111 --> 转换为十进制 127( 2 7 − 1 2^7 - 1 27−1 )
-
8 位可表示的最小二进制数位 10000000 --> 转换为十进制 -128 ( 2 7 2^7 27 )
-
byte 的取值范围: -128 ~ 127
Byte.MIN_VALUE Byte.MAX_VALUE
-
-
short
-
在 Java 中 short 占 16 位
-
short 的取值范围: ( 2 16 − 1 ) (2^{16} - 1) (216−1) ~ 2 16 2^{16} 216
Short.MIN_VALUE Short.MAX_VALUE
-
-
int
-
占 32 位
-
取值范围: ( 2 32 − 1 ) (2^{32}-1) (232−1) ~ − 2 32 -2^{32} −232
Integer.MIN_VALUE Integer.MAX_VALUE
-
-
long
-
占 64 位
-
取值范围: ( 2 64 − 1 ) (2^{64} - 1) (264−1) ~ 2 64 2^{64} 264
Long.MIN_VALUE Long.MAX_VALUE
-
-
char
char 类型占 64 位,但是 char 类型没有 符号位(也称 无符号),取值范围 在 0 ~ ( 2 16 − 1 ) (2^{16} - 1) (216−1) 之间。
4.4 二进制的浮点数
-
现代计算机一般以 IEEE 754 标准存储浮点数
对于 float 类型: 占 32 位,数符分配是 1 位,阶码分配是 8 位,尾数分配是 23 位。
数符 阶码 尾数 总尾数 偏移值 短实数 1 8 23 32 127 长实数 1 11 52 64 1023 临时实数 1 15 64 80 16383 -
示例: 178.125 转换为 二进制
-
先把整数部分和小数部分分别转换为 二进制
- 整数部分:10110010
- 小数部分:001
- 合起来:10110010.001
- 转换为二进制浮点数,即把小数点位移到整数位只有 1,即为: 1.01100100001 ∗ 2 111 1.01100100001 * 2^{111} 1.01100100001∗2111 ( 111是 二进制,左移了 7 位,转换为二进制 就是 111)
-
把浮点数转换二进制后,对应 3 部分的值
- 数符:正数,所以是 0
- 阶码:阶码的计算公式( 阶码 ( 111 )+偏移量( 01111111 ) ) ( 111 + 01111111 = 10000110 ) (111 + 01111111 = 10000110) (111+01111111=10000110)
- 尾数:小数点后面的数( 011001001 )
-
最终展示:
-
4.5 Java 浮点数
-
Java 中浮点数 float 和 int 内存中都占用 32 位,但是 float 的范围要比 int 更大(原因参照上面二进制的浮点数,由于阶码的存在,同样的位数,浮点数可以表示更大的范围),但是也导致 当数值太大以后,尾数位不够大,导致 float 的精度将会下降,所以 虽然 float 类型的范围比 int 大,但是 int 转换为 float 时,可能会出现 精度丢失的情况。
1.23456792E8 ( 1.23456792 ∗ 1 0 8 1.23456792 * 10^8 1.23456792∗108 )
int n = 123456789; float f = n; System.out.println(n);// 123456789 System.out.println(f);// 1.23456792E8
5. 位运算符
5.1 非( ~ )
非运算:按位取反。
( int 为 32 位,前面的 0 省略 )
x 1 1 1 1 1 0 1 0 5 0 0 0 0 0 1 0 1
int x = ~5; // -6
5.2 与( & )
与运算:只有当两个操作数都为 1 的时候才取 1,否则就取 0。
( int 为 32 位,前面的 0 省略 )
x 0 0 0 0 0 1 0 0 6 0 0 0 0 0 1 1 0 5 0 0 0 0 0 1 0 1
int x = 5 & 6; // 4
5.3 或( | )
或运算:只要有一个操作数为 1 结果就为 1。
( int 为 32 位,前面的 0 省略 )
x 0 0 0 0 0 1 1 1 5 0 0 0 0 0 1 0 1 6 0 0 0 0 0 1 1 0
int x = 5 | 6;// 7
5.4 异或( ^ )
异或运算:当两个操作数不同时,结果为 1;两个操作数相同时,结果为 0。
( int 为 32 位,前面的 0 省略 )
x 0 0 0 0 0 0 1 1 5 0 0 0 0 0 1 0 1 6 0 0 0 0 0 1 1 0
int x = 5 ^ 6; // 3
5.5 右移( >> )
> > >> >> 移位运算符:将所有操作数向 右 位移 n 位,前面空出的用 符号位 补足( 正数补 0,负数 补 1 ),移出的舍去。
正数右移相当于十进制除法运算除以 2 n 2^n 2n 。
( int 为 32 位,前面的 0 省略 )
5 0 0 0 0 0 1 0 1 x 0 0 0 0 0 0 1 0 y 0 0 0 0 0 0 0 1 int x = 5 >> 1;// 2 int y = 5 >> 2; // 1
5.6 左移( << )
< < << << 移位运算符:将所有操作数向左 位移 n 位,后面补足 0。
正数左移相当于十进制乘法运算操作数乘以 2 n 2^n 2n 。
( int 为 32 位,前面的 0 省略 )
5 0 0 0 0 0 1 0 1 x 0 0 0 0 1 0 1 0 y 0 0 0 1 0 1 0 0 int x = 5 << 1; // 10 int y = 5 << 2; // 20
5.7 无符号右移( >>> )
> > > >>> >>> 无符号右移:将所有操作数向 右 位移 n 位,前面空出的用 0 补足,对于正数 与 > > >> >> 运算相同,对于负数 > > > >>> >>> 运算后将变为正数。
5.8 问题
-
有 10 瓶药,其中有 若干瓶 坏药,已知 好药每颗重 1 克,坏药每颗重 0.9 克,有一个秤,只能秤一次,找到所有的坏药。
药瓶编号 1 2 3 4 5 6 7 8 9 10 取出药数 2 1 2^1 21 2 2 2^2 22 2 3 2^3 23 2 4 2^4 24 2 5 2^5 25 2 6 2^6 26 2 7 2^7 27 2 8 2^8 28 2 9 2^9 29 2 10 2^{10} 210 实际重量 1 1 0 1 1 0 1 1 0 1 最大重量 1 1 1 1 1 1 1 1 1 1