进制
进制基础知识
进制,指进制位,是一种记数方式,也称为进位记数法或位值记数法。
- 十进制:0,1,2,3,4,5,6,7,8,9
- R进制:由 0~(R-1) 组成,并且每一位上的数据逢 R 进 1;
我们的计算机是由逻辑电路组成,逻辑电路通常只有两个状态:开关的接通与断开;
这两种状态正好可以用 “1” 和 “0” 表示,也就是我们要讲解的组成二进制的数字符号。
- 二进制:0,1
虽然计算机喜欢二进制数据,但是用二进制表示数据太长了,为了解决这个问题,八进制和十六进制就来了。
- 八进制:0,1,2,3,4,5,6,7
- 十六进制:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F(不区分大小写)
进制越大,表示形式越短。
为了区分不同进制数值的表示,常常在开头处加以区分:
规则:按权展开法(系数 * 基数的权次幂相加)
- 系数:每一位上的数;
- 基数:R 进制,基数就是 R;
- 权:从数值的右侧,以 0 开始编号,对应位上的编号就是该位上的权;
十进制到 R 进制
规则:重复相除法(除基取余,直到商为 0,余数反转)
进制之间的快速转换
二进制到十进制
采用 8421 码是 BCD 代码中最常用的一种。
BCD:(Binary-Coded Decimal)二进制码十进制数,在这种编码方式中,每一位二进制值代码的 1 都是代表一个固定数值,把每一位的 1 代表的十进制加起来得到的结果就是它所代表的十进制数码。
示例:
二进制 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
---|---|---|---|---|---|---|---|---|
十进制 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
二进制到八进制
三位组合法:
- 把二进制的数据,从右开始,每三位一组合,最左边不够的时候补 0;
- 然后,分别计算对应的十进制数值;
- 最后,从左往右,把每个十进制的数据组合起来,就是一个八进制数据;
二进制到十六进制
四位组合法:
- 把二进制的数据,从右开始,每四位一组合,最左边不够的时候补 0;
- 然后,分别计算对应的十进制数值;
- 最后,从左往右,把每个十进制的数据组合起来,就是一个十六进制数据;
注释,10 - 15 的数值要用 A - F 替换
Java 内置的进制转换
java.lang.Integer 类中的静态方法:
- public static String toBinaryString(int i):在基数 2 中返回整数参数的字符串表示形式为无符号整数;
- public static String toOctalString(int i):在基数 8 中返回整数参数的字符串表示形式为无符号整数;
- public static String toHexString(int i):返回整数参数的字符串表示形式,作为16位中的无符号整数;
- public static String toString(int i, int radix):返回由第二个参数指定的基数中的第一个参数的字符串表示形式;
有符号数据表示法
十进制数据:
- +表示正数
- -表示负数
计算机中的数据:
- 0表示正数
- 1表示负数
而对于计算机识别的数据来说,0和1本身也表示数据值,那么我们怎么判断它是数值位还是符号位呢?
规定:符号位位于数值第一位。
其实,这就是我们有符号数据表示法的一种表示形式,叫原码表示法。
原码表示法
最简单的机器数表示法。
- 1 表示负号,0 表示正号。
- 用最高位表示符号位,其余位表示数值的大小、
计算:
1 + 2 = 00000001 + 00000010 = 00000011 = 3
1 - 2 = 1 + (-2) = 00000001 + 10000010 = 10000011 = -3 X
反码表示法
- 正数的反码和原码相同
- 负数的反码就是它的原码除符号位以外,按位取反(1 变成 0,0 变成 1)
计算:
1 - 2 = 1 + (-2) = 00000001 + 11111101 = 11111110 = -1
-1 - 2 = -1 + (-2) = 11111110 + 11111101 = 11111011(符号位进位舍弃)= -4 X
补码表示法
- 正数的补码和原码相同;
- 负数的补码等于反码加一。即它的原码除符号位以外,按位取反,末位加一;
计算:
1 - 2 = 1+ (-2) = 00000001 + 11111110 = 11111111 = -1
-1 - 2 = (-1) + (-2) = 11111111 + 11111110 = 11111101(符号位进位舍弃) = -3
结论:在计算机中,存储和运算采用的都是补码进行的,计算过程中,符号进位舍弃。
整数强制转换之数据溢出
byte b = (byte) 130;
System.out.println(b); // -126
下面看一下运算步骤:
- 首先我们将 130 还原为计算机存储的二进制形式,也就是补码形式。
- 130 默认为 int 类型 占 4 个字节,对应的二进制原码为:
00000000000000000000000010000010
- 130 对应的补码:
00000000000000000000000010000010
- 强制转换为 byte 类型。则补码:
10000010
- 反码:
10000001
- 原码:
11111110
- 最高位 1 为符号位,通过 8421 码得到的十进制数值为:-126
最终130 强转为 byte 类型结果输出为 -126
浮点数进制间的转换
https://www.bilibili.com/read/cv11242656/
二进制浮点数转换为十进制浮点数
规则:按权展开法(系数 * 基数的权次幂相加)
- 系数:每一【位】上的数
- 基数:R 进制,基数就是 R
- 权:
- 整数部分:从小数点左侧,以 0 开始编号,对应位上的编号就是该位上的权
- 小数部分:从小数点右侧,以 -1 开始编号,对应位上的编号就是该位上的权
十进制浮点数转换为二进制浮点数
规则:整数部分重复相除法,小数部分重复相乘法
- 整数部分重复相除法:除基取余,直到商为 0,余数反转;
- 小数部分重复相乘法:乘基取整,直到小数位 0 或者达到指定精度位,整数顺序排列。
举例:58.625 转换成 二进制数 111010.101
# 整数部分
58 / 2 = 29...0
29 / 2 = 14...1
14 / 2 = 7....0
7 / 2 = 3.....1
3 / 2 = 1.....1
1 / 2 = 0.....1 # ↑ 商为0,余数反向取值:111010
# 小数部分
0.625 * 2 = 1.25...1
0.25 * 2 = 0.5 ...0
0.5 * 2 = 1.0 ...1 # ↓ 小数位为0或满足精度要求,正向取值:101
# 最终二进制:111010.101
小数部分,对于有些数字,无法计算到小数位为 0,这时就会有精度丢失问题。例如 0.1
# 整数部分
0 / 2 = 0...0
# 小数部分
0.1 * 2 = 0.2...0
0.2 * 2 = 0.4...0
0.4 * 2 = 0.8...0
0.8 * 2 = 1.6...1
0.6 * 2 = 1.2...1
0.2 * 2 = 0.4...0
0.4 * 2 = 0.8...0
......
浮点数存储
根据国际标准 IEEE754,任意一个二进制浮点数都可以表示为:
%5Es%20%20M%20%202%5EE%0A#card=math&code=V%20%3D%20%28-1%29%5Es%20%2A%20M%20%2A%202%5EE%0A&id=KD3Oa)
- s 表示符号位,当 s = 0,V 为正数,当 s = 1,V 为负数;
- M 表示有效数字;
- E 表示指数位;
举例:十进制的浮点数位 6.225,对应二进制位 110.101 相当于 1.10101*2^2
按照上面 V 的格式,可以得出:s = 0, M = 1.10101, E = 2
IEEE 754 对浮点数存储的规定:
对于 32 位浮点数,也就是 float 类型的,最高的 1 位是符号位 s。接着的 8 位是指数 E。剩下的 23 位是有效数字 M;
对于 64 位浮点数,也就是 double 类型的,最高的 1 位是符号位 s。接着的 11 位是指数 E。剩下的 52 位是有效数字 M;
IEEE 754 对有效数字 M 和指数 E,还有一些特别规定:
规范化表示的时候,M 要写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分
IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1,因此可以被舍去,只保存后面的 xxxxxx 的部分;
举例:保存 1.01 的时候,只保存 01,等到读取的时候,再把第一位的 1 加上去。
指数 E,为一个无符号整数(unsigned int)。这意味着,如果 E 为 8 位,它的取值范围为 0 ~ 255,如果 E 为 11 位,它的取值范围为 0 ~ 2047;但是我们知道,科学计算法中的 E 是可以出现负数的,所以 IEEE 754 规定,E 的真实值必须再减去一个中间数,对于 8 位的 E,这个中间数是 127, 对于 11 位的 E,这个中间数是 1023。
举例:2^10 的 E 是 10,所以保存为 32 为浮点数时,必须保存成 10 + 127 = 137,即 10001001