https://www.bilibili.com/video/BV1EJ41187Nv

进制

进制基础知识

进制,指进制位,是一种记数方式,也称为进位记数法或位值记数法。

  • 十进制: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(不区分大小写)

进制越大,表示形式越短。

为了区分不同进制数值的表示,常常在开头处加以区分:

  • 二进制:在Java中以 0b 开头;
  • 八进制:在Java中以 0 开头;
  • 十进制:整数默认是十进制的;
  • 十六进制:在Java中以 0x 开头;

    进制转换

    R 进制到十进制

规则:按权展开法(系数 * 基数的权次幂相加)

  • 系数:每一位上的数;
  • 基数: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

结论:在计算机中,存储和运算采用的都是补码进行的,计算过程中,符号进位舍弃。

整数强制转换之数据溢出

  1. byte b = (byte) 130;
  2. 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

  1. # 整数部分
  2. 58 / 2 = 29...0
  3. 29 / 2 = 14...1
  4. 14 / 2 = 7....0
  5. 7 / 2 = 3.....1
  6. 3 / 2 = 1.....1
  7. 1 / 2 = 0.....1 # ↑ 商为0,余数反向取值:111010
  8. # 小数部分
  9. 0.625 * 2 = 1.25...1
  10. 0.25 * 2 = 0.5 ...0
  11. 0.5 * 2 = 1.0 ...1 # ↓ 小数位为0或满足精度要求,正向取值:101
  12. # 最终二进制: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
......

最终结果为 0.000110001100011……

浮点数存储

根据国际标准 IEEE754,任意一个二进制浮点数都可以表示为:

Java 进制 - 图1%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