Java修炼之凡界篇 筑基期 第02卷 语法 番外1 原码 反码 补码

本文详细解读了机器数、真值概念,以及原码、反码和补码在计算机中的应用,解释了为何需要这些编码方式,重点讲解了原码、反码计算方法,以及补码如何解决负数表示和溢出问题,以12进制钟表为例深入剖析了同余原理在编码中的运用。
摘要由CSDN通过智能技术生成

原码 反码 补码

博客整理自

原码, 反码, 补码 详解 - ziqiu.zhang - 博客园 (cnblogs.com)

机器数与真值

在学习原码、反码和补码之前,需要先了解什么是机器数,什么是真值

机器数

  • 一个数在计算机中的二进制表示形式,叫做这个数的机器数
  • 机器数是带符号的,在计算机用一个数的最高位存放符号,正数为0,负数为1,计算机字长为8的整数倍,最小字长是8位
  • 十进制数 +3 ,转换成二进制是 00000011
  • 十进制数 -3 ,转换成二进制是 10000011
  • 00000011 和 10000011 我们叫它机器数

真值

  • 由于第一位为符号位,所以机器数就不等于真正的数值
  • 10000011 机器数真正的数值是-3,而不是直接换算为十进制的 131
  • 为了进行区分,将带有符号位的机器数对应的真正数值称为真值
  • 0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

原码、反码和补码基础概念及计算方法

在知道计算机为什么要使用补码之前,让我们先了解原码,反码和补码的概念
对于一个数, 计算机要使用一定的编码方式进行存储,原码、反码和补码是机器存储一个具体数字的编码方式

原码

  • 原码就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值
  • 以8位二进制数为例,[+1]原 = 0000 0001
  • 以8位二进制数为例,[-1]原 = 1000 0001
  • 因为第一位是符号位,所以8位二进制数的取值范围就是[1111 1111, 0111 1111],即[-127, 127]
  • 原码是人最容易理解和计算的表示方式

反码

  • 正数的反码是原码本身
  • 负数的反码是在原码基础上,符号位不变,其余各位数取反
  • 以8位二进制数为例,[+1] = [00000001]原 = [00000001]反
  • 以8位二进制数为例,[-1] = [10000001]原 = [11111110]反
  • 一个反码表示的是负数,人无法直观看出它的数值,通常要将它转换成原码再计算

补码

  • 正数的补码就是原码本身
  • 负数的补码是在原码的基础上,符号位不变,其余各位取反,最后+1(即在反码的基础上+1)
  • 以8位二进制数为例,[+1] = [00000001]原 = [00000001]反 = [00000001]补
  • 以8位二进制数为例,[-1] = [10000001]原 = [11111110]反 = [11111111]补
  • 一个补码表示的是负数,人无法直观看出它的数值, 通常要将它转换成原码再计算

为什么要使用原码、反码和补码

计算机可以有三种编码方式表示一个数,对于正数三种编码方式结果都相同,负数的原码、反码和补码是完全不相同的

原码缺陷

  • 既然原码才是被人直接识别并用于计算的,为什么还需要有反码和补码呢?
  • 人可以知道第一位是符号位,在计算时会根据符号位,选择对真值进行加减
  • 相对于计算机来说,加减乘除是最基础的运算,计算机辨别“符号位”会让计算机基础电路设计变得复杂
  • 人们想出了将符号位也参与运算的方法,运算法则得知,减去一个整数等于加上一个负数,即 1 - 1 = 1 + (-1) = 0
  • 计算机可以需要加法而不需要减法,这样使得计算机的电路设计变得更加简单
  • 于是人们开始探索,将符号位参与运算,并且只保留加法的方法
  • 以十进制为例,1 - 1 = 0,即 1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
  • 如果仅仅只是用原码进行表示,很明显结果计算的结果不正确,这也就是为什么计算机内部不使用原码表示数值

反码出现及缺陷

  • 为了解决原码做减法的问题,出现了反码
  • 以十进制为例,1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
  • 使用反码进行计算,虽然结果的真值部分正确,但是唯一的问题出现在结果值0这个特殊值上,人们理解的+0和-0结果是一样的,但是0带符号是没有意义的,而且有00000000和10000000两个原码表示,很明显结果还是不正确

补码出现

  • 补码的出现解决了0的符号以及两个编码的问题
  • 以十进制为例,1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
  • 0用[0000 0000]表示,-0的问题也得到了解决,且可以用[1000 0000]表示-128
  • 即(-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类型,可以表示的范围就是 [-231, 231-1] ,因为第一位表示的是符号位,而是用补码表示时,又可以多保存一个最小值

原码、反码和补码深入解析

举例解析

  • 将钟表想象成是一个1位的12进制数
  • 如果当前时间是6点,我希望将时间设置成4点,我们可以怎么做
  • 第一种,往回拨2个小时: 6 - 2 = 4
  • 第二种,往前拨10个小时: (6 + 10) mod 12 = 4
  • 第三种,往前拨10+12=22个小时: (6+22) mod 12 =4
  • 第二种和第三种是指取模运算(求余),16 mod 12 = 4 即用16除以12后的余数是4
  • 钟表往回拨(减法)的结果可以用往前拨(加法)来替代
  • 那么关键点就在于如何使用正数去替代负数
  • 在数学计算中有一个叫同余的概念

同余

  • 两个整数a,b,如果它们除以整数m所得余数相等,则称a和b对于模m同余
  • 即 a = b (mod m),读作a与b关于模m同余
  • 4 mod 12 = 4,16 mod 12 = 4,28 mod 12 = 4,所以4,16,28模12同余

负数取模

  • 正数取模相对简单,那负数呢?

  • 下面是数学中定义的取模运算公式,x mod y = x - y ⌊ x / y ⌋,⌊ x / y ⌋就是得到的商向下取整

    • 向下取整运算称为Floor,用数学符号⌊⌋表示
    • 向上取整运算称为Ceiling,用数学符号⌈⌉表示
    • 向上取整(Ceiling),不管四舍五入规则,只要数值后面有小数,则前面的整数就加1,得到结果值
    • 向下取整(Floor),不管四舍五入规则,只要数值后面有小数,则后面的小数忽略,取整数部分作为结果值
    • 举例:正数1.5,向上取整值为2,向下取整值为1,负数-1.5,向上取整值为-1,向下取整值为-2,0无论向下取整还是向上取整都为0
    • 由此可以得知,正负数向上向下取整是相反的
  • 此处以-3 mod 2 举例:

    -3 mod 2

    = -3 - 2 × ⌊ -3/2 ⌋

    = -3 - 2 × ⌊ -1.5 ⌋

    = -3 - 2 × (-2)

    = -3 + 4 = 1

  • 相对应的 -2 mod 12 = 10,-4 mod 12 = 8, -5 mod 12 = 7

  • 接着依然使用将钟表想象成是一个1位的12进制数,进行问题探讨

    • 10点钟 ,往回拨2小时 = 往前拨10小时
    • 8点钟,往回拨4小时 = 往前拨8小时
    • 7点钟,往回拨5小时 = 往前拨7小时
  • 根据这个规律,结合同余的概念,则有

    -2 mod 12 = 10

    10 mod 12 = 10

    -2 与 10 同余

    -4 mod 12 = 8

    8 mod 12 = 8

    -4 与 8 同余

  • 要实现正数替代负数,需要用到同余的反身性 a = a (mod m) , 同余式相加减,若a ≡ b (mod m),c ≡ d (mod m), 则 a ± c = b ± d (mod m);同余式相乘,若a ≡ b (mod m),c ≡ d (mod m), 则 a × c = b × d (mod m)

    7 ≡ 7 (mod 12)

    -2 ≡ 10 (mod 12)

    7 - 2 ≡ 7 + 10 (mod 12)

    这样就可以将负数用正数来进行表示

  • 返回到最初的二进制问题,2 - 1 = 1

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

    • -1 的反码为 1111 1110,假设将 -1 的反码认为是原码,则 [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]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫在问

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值