汇编-04-数据格式

汇编-04-数据格式

1. 数据宽度

  • bit : 1位
  • Byte : 8 位
  • Word : 16 位
  • DWord : 32 位

java需要定义数据的类型

  • 位: 0 1 (1位)
  • 字节:0 - 0xFF (8位)
  • 字 : 0 - 0xFFFF (16位)
  • 双字:0 - 0xFFFF FFFF (32位)

2. 有符号数和无符号数

本质是为了数据的正负号

  • 无符号数

    • 举个例子

      1 0 0 1 1 0 1 0

      • 十六进制 0x9A

      • 十进制 154

  • 有符号数(最高位1:负数 最高位0:负数)

    • 最高位是符号位

    • 举个例子

      1 0 0 1 1 0 1 0

      • 十六进制:???
      • 十进制:???
      • 这里???是为了引出(源码,反码和补码的概念)

3. 原码,反码,补码

  • 编码规则
    • 有符号的编码规则
    • 原码:最高位是符号位,其他位就是自己的绝对值
    • 反码
      • 正数:反码和原码一样的
      • 负数:符号位一定是1,其他位对原码取反。
    • 补码
      • 正数:反码和补码相同
      • 负数:符号位一定是1,其他位对原码取反后 + 1(也就是对反码加1)

测试:这都是二进制的规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
8位举例

1. 如果是正数,都是一样的
写出 1 的原码,反码,补码
原码:0000 0001
反码:0000 0001
补码:0000 0001


2. 如果是负数 -1
写出 -1 的原码,反码,补码
原码:1000 0001
反码:1111 1110
补码:1111 1111

3. 再来一个例子 -7
写出 -7 的原码,反码,补码
原码:1000 0111
反码:1111 1000
补码:1111 1001

4. ff(有符号)
原码 : 1111 1111
反码 : 1000 0000
补码 : 1000 0001

这些知识都是为了后面的位运算。

4. 为什么要使用原码, 反码和补码

现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:

1
[+1] = [00000001]原 = [00000001]反 = [00000001]补

所以不需要过多解释. 但是对于负数:

1
[-1] = [10000001]原 = [11111110]反 = [11111111]补

可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?

对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别”符号位”显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.

  1. 如果使用原码进行计算

于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:

1
2
计算十进制表达式 1 - 1= 0
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2

如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.

  1. 使用反码的话
1
2
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0

发现如果使用反码进行计算,结果的真值部分是正确的。而唯一的问题其实就出现在”0” 这个特殊的数值上,虽然人们理解的+0 和 -0 是一样的,但是0带有符号是没有意义的,而且会出现[0000 0000]原和[1000 0000]原两个编码表示0.

  1. 于是补码的出现解决了0的符号和两个编码的问题
1
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原

这样的话 + 0和 -0的问题就不存在了,而且可以使用[1000 0000] 表示 -128

1
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
  • -1 - 127的结果应该是-128 , 在用补码运算的结果中, [1000 0000]补 就是-128.

  • 但是注意因为实际上是使用以前的 - 0 来表示 -128 ,所以 -128 没有原码和反码的表示。(对 -128的补码表示[1000 0000]补 算出来的原码[0000 0000]原,这是不正确的)

  • 使用补码的话,不仅仅修复了 0 的符号存在的两个编码的表示,而且还能多表示一个最低数字,这就是为什么8位二进制,使用原码或者反码的表示范围是[-127,127]。但是使用补码表示的范围就是[-128,127]

  • 因为计算机使用补码,所以对于编程中常用的32位int类型,可以表示的范围的范围是[-2^31,2^31-1] 因为第一位表示的是符号位,而使用补码表示时又可以多保存一个最小值。

5. 原码,反码,补码再深入

  • 计算机巧妙地把符号位参与运算, 并且将减法变成了加法, 背后蕴含了怎样的数学原理呢?
  • 将钟表想象成一个1位的12进制数,如果当前时间是6点,我希望时间设置为4点,需要怎么做?
      1. 往回拨2个小时 6 -2 = 4
      2. 往前拨10个小时 (6+10)mod 12 = 4
      3. 往前拨10 + 12 = 12 个小时 (6+22) % 12 = 4
    • 2,3方法中的mod是指取模操作, 16 mod 12 =4 即用16除以12后的余数是4.
    • 所以钟表往回拨(减法)的结果可以用往前拨(加法)替代!
    • 现在问题的焦点是如何用一个正数,来替代一个负数,上面的例子我们能感觉出来一些端倪, 发现一些规律. 但是数学是严谨的. 不能靠感觉.
  • 首先介绍一个数学中的概念:同余

    • 两个正数a,b 若它们整除m所得的余数相等,则称 a, b 对于模m同余
    • 记作 a ≡ b (mod m)
    • 读作 a 与 b 关于模 m 同余。

    举例说明

    1
    2
    3
    4
    5
    4 mod 12 = 4

    16 mod 12 = 4

    28 mod 12 = 4

    所以4, 16, 28关于模 12 同余.

  • 负数取模

    • 正数进行mod运算是很简单的. 但是负数呢?

    • 下面是关于mod运算的数学定义:

      mark

      • x mod y = x - yL x/y J(上面是截图, “取下界”符号找不到如何输入(word中粘贴过来后乱码). 下面是使用”L”和”J”替换上图的”取下界”符号:)

      • x mod y等于 x 减去 y 乘上 x与y的商的下界.

      • 以 -3 mod 2 举例:

        -3 mod 2

        = -3 - 2xL -3/2 J

        = -3 - 2xL-1.5J

        = -3 - 2x(-2)

        = -3 + 4 = 1

      • (-2) mod 12 = 12-2=10

        (-4) mod 12 = 12-4 = 8

        (-5) mod 12 = 12 - 5 = 7

  • 再回到时钟问题

    • 回拨2小时 = 前拨10小时

      回拨4小时 = 前拨8小时

      回拨5小时= 前拨7小时

    • 注意, 这里发现的规律!

      结合上面学到的同余的概念.实际上:

    • (-2 ) mod 12 = 10

      10 mod 12 = 10

      所以说 -2 和 10 是同余的

    • 同时, -4 和 8 也是同余的

      (-4 )mod 12 = 8

      8 mod 12 = 8

  • 要实现用正数替代负数, 只需要运用同余数的两个定理:

    • 反身性:这个定理是很显而易见的.

      a ≡ a (mod m)

    • 线性运算定理

      如果a ≡ b (mod m),c ≡ d (mod m) 那么:

      (1)a ± c ≡ b ± d (mod m)

      (2)a * c ≡ b * d (mod m)

      如果想看这个定理的证明, 请看:http://baike.baidu.com/view/79282.htm

    • 所以说:

      7 ≡ 7 (mod 12)

      (-2) ≡ 10 (mod 12)

      7 -2 ≡ 7 + 10 (mod 12)

    • 现在我们为一个负数,找到了它的正数的同余数,但是并不是 7 -2 = 7 + 10 ,而是 7 -2 ≡ 7 + 10 (mod 12) , 即计算结果的余数相等.

  • 接下来回到二进制的问题上, 看一下: 2-1=1的问题.
    • 2-1=2+(-1) = [0000 0010]原 + [1000 0001]原= [0000 0010]反 + [1111 1110]反
    • 先走到这一步, -1 的反码表示 是 1111 1110 ,如果这里将[1111 1110]认为是原码,那么[1111 1110] = -126 ,这里将符号位除去,即认为是 126
    • 发现如下规律
      • (-1) mod 127 =126
      • 126 mod 127 = 126
    • 即有如下表达式
      • (-1) ≡ 126 (mod 127)
      • 2 - 1 ≡ 2 + 126(mod 127)
      • 2-1 与 2+126的余数结果是相同的! 而这个余数, 正式我们的期望的计算结果: 2-1=1
  • 所以说一个数的反码,实际上是这个书对于一个数模的同余数,而这个模并不是我们的二进制,而是所能表达的最大值。

  • 这就和钟表是一样的了,转了一个圈之后,总能在可表达范围内找打一个正确的数值。

  • 2 + 126 很明显先当与钟表过了一轮,而符号位是参与计算的,正好和溢出的最高位形成正确的结果

  • 既然反码可以将减法变成加法。那么现在计算机使用的补码呢?为什么在反码的基础上加1 ,还能得到正确的结果?

    • 2-1=2+(-1) = [0000 0010]原 + [1000 0001]原 = [0000 0010]补 + [1111 1111]补

      如果将 [1111 1111] 当作原码,去除符号位,则 [0111 1111] = 127

      其实 在反码的基础上 + 1 就相当于增加了模的值

      (-1) mod 128 = 127

      127 mod 128 = 127

      2-1 ≡ 2+127 (mod 128)

  • 此时,表盘相当于每128个刻度转一轮,所以用补码表示的结果最大值和最小值[-128, 128].

  • 但是由于0的特殊情况,没有办法表达128,所以补码的范围是[-128,127]

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信