关于二进制的原码、补码和反码,以及表示范围、常见位运算符和进制转换的理解与简述

这篇具有很好参考价值的文章主要介绍了关于二进制的原码、补码和反码,以及表示范围、常见位运算符和进制转换的理解与简述。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/17963363
出自【进步*于辰的博客】

参考笔记一,P3.13、P5.1;笔记三,P43.1/3、P44.1。

注:我暂且没有整理关于二进制、原码、补码和反码等概念的理论,本文中的阐述都基于我对相应概念的理解,推荐两篇博文(转发):

  1. 关于对【8位二进制的整数表示范围、常见位运算符和进制间转换】的理解与简述;
  2. 原码、反码、补码知识详细讲解(此作者是我找到的讲的最细最明白的一个)。

这两篇文章都是对我的启发之作,一些概念(如:机器数)也出自于此,建议大家先去浏览这两篇博文,这样会更便于阅读本文。

目录
  • 1、二进制相关概念
    • 1.1 原码、反码
    • 1.2 补码
  • 2、八位二进制的表示范围
  • 3、常见位运算符
    • 3.1 取反 ~
    • 3.2 左移 <<
    • 3.3 有符号右移 >>
    • 3.4 无符号右移 >>>
    • 3.5 按位与 &
    • 3.6 按位或 |
    • 3.7 按位异或 ^
    • 3.8 扩展
  • 4、位运算运用示例
    • 4.1 左移与算术右移的巧妙运用
    • 4.2 实现字符大小写转换
    • 4.3 生成 IP 地址
  • 5、关于进制间转换
  • 最后

1、二进制相关概念

在研究8位二进制的表示范围之前,需要先了解原码、反码和补码这三个概念。

1.1 原码、反码

“原码”指符号位加上“真值"绝对值的“机器数”。

反码定义:(摘录自第一篇启发之作)

正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。

1.2 补码

先说说补码定义:

正数的补码是其本身的原码;负数的补码是其反码+1后的结果。

补码是做什么的?(摘录自第一篇启发之作)

引入补码是为了解决计算机中数的表示和数的运算问题,使用补码,可以将符号位和数值域统一处理,即引用了模运算在数理上对符号位的自动处理,利用模的自动丢弃实现了符号位的自然处理,仅仅通过编码的改变就可以在不更改机器物理架构的基础上完成预期的要求。

我用几个示例简单说明一下我这段阐述的理解。

示例11 + (-2) = -1
看下述示例。

1 + (-2) = 0000 0001 + 1000 0010 = 1000 0011,得:-3

结果显然不对,是因为没有考虑到符号位,符号位不能直接参与运算。
那若是考虑符号位应如何?看示例。

由于-2的绝对值大于1,故:
1 + (-2) = -(2 - 1) = 1(000 0010 - 000 0001) = 1000 0001,得:-1

这显然增加了硬件的开销和复杂性。(本人对硬件了解有限,所以暂且不知此结论的出处)

若引入补码:

求-2的补码:-2的原码1000 0010 → 反码1111 1101 → 补码1111 1110
求1的补码:1是正数,故就是其原码0000 0001
计算:1 + (-2) = 0000 0001 + 1111 1110 = 1111 1111
由于都是补码,所以1111 1111也是补码,
1111 1111减1 → 反码1111 1110 → 原码1000 0001,真值为:-1

从此示例可看出,补码将符号位和数值域统一处理。换言之,不需要考虑正负大小情况。

示例2
11 + 256 = 1
256是8位无符号二进制的,其机器数为1 0000 0000。这是如何得出的?
8位无符号二进制的最大机器数是1111 1111,进行+1,得1 0000 0000,但只有8位,最高位1自然丢失。得00000000。因此,1 + 256 = 1 + 0 = 1
这就是上文所述的“模的自动丢弃实现了符号位的自然处理”。

21 + 128 = 1
128是8位有符号二进制的,其机器数为1000 0000(理论上)。这又是怎么个说法?
先透露:8位有符号二进制无法表示128
无妨,可暂且将其视为无符号二进制进行计算。128 = 127 + 1127的补码是0111 1111,进行+1,得1000 0000
那么,1 + 128 = 0000 0001 + 1000 0000,得1000 0001。不过,我们只是将其视为无符号,实际有符号,故符号位无效,不进行计算。因此,结果为0000 0001,真值为1

注意:在计算机底层,数都是以补码的形式进行表示。


扩展一点:
在查阅有关补码的资料时,我注意到一些文章中阐述的负数补码的另一种计算方法:

绝对值原码 → 取反 → +1

综本文所述,补码的计算方法:

原码 → 反码 → +1

另一种方法可行吗?
经过观察,结论是:当然可行。

负数绝对值原码与负数原码的不同唯有符号位,如:1 = 0000 0001-1 = 1000 0001,故负数绝对值原码取反与负数反码相同。

因此,两种方法都可行,大家觉得怎么方便怎么来。

2、八位二进制的表示范围

1、无符号二进制。
既然“无符号”,则无正负之分,都表示正数。

  1. 0000 0000 → 0;
  2. 1111 1111 → 27 + 26 + ... + 21 + 20 = 255。

因此,8位无符号二进制的表示范围是0 ~ 255

2、有符号二进制。

关于8位有符号二进制的表示范围,其中细节比较复杂,暂不讨论,我暂且简述,详述可查阅第一篇启发博文。

分析:
8位二进制的范围是0000 0000 ~ 1111 1111

  1. 正区间0000 0000 ~ 0111 1111,即0 ~ 127
  2. 负区间1000 0000 ~ 1111 1111,首先,1111 1111 ~ 1000 0001-127 ~ -1,那1000 0000的真值是多少?

1000 0000 = 1000 0001 - 1(无视符号位,这样等式才成立),可实际上都表示负数,则是+11000 0001-1,故1000 00000
0明明是0000 0000?那么,可视为+0,则1000 0000-0
从上文【示例2】可知,8位有符号二进制的128。因此,-0等同于-128。所以,1000 0000的真值为-128

补充说明
上述推导基于原码,当然也可以使用补码,只是原码更易于理解。
如下:
1000 0000 = 1000 0001 - 0000 0001(由于都是补码,故等式也成立),1000 0001-127,则1000 0000-128

为什么是补码,等式就成立?
“引入补码”的目的之一是为了解决数的运算问题,比如:-1 + 2,用原码运算时就要考虑绝对值的大小问题。引入补码后就无需考虑,故可以说“负数的补码是正数”(当然负数的补码还是负数,只是视为正数)。

因此,8位有符号二进制的表示范围是-128 ~ 127

3、常见位运算符

~ << >> >>> & | ^
取反 左移 有符号右移 无符号右移 按位与 按位或 按位异或

先说明:

  1. 下文中的“高位”指第一位,“低位”指最后一位。(左 → 右)
  2. 二进制在计算机中都是以补码的形式进行表示。因此,以下示例都要遵循补码定义。

3.1 取反 ~

运算规则:逐位反转,即:0 → 1, 1 → 0

示例:计算~23

23的补码:0001 0111
~23 = 1110 1000
1110 1000 → 反码1110 0111 → 原码1001 1000,真值为-24

扩展一点:
在运算规则上,取反~与反码相同。不过,~不考虑符号位,与反码定义不同。

3.2 左移 <<

运算规则:去高位,补低位,补0

示例:
1、计算23 << 1

23的补码:0001 0111
23 << 1 = 0010 1110,真值为46

2、计算 -23 << 1

计算-23的补码:-23原码1001 0111 → 反码1110 1000 → 1110 1001
-23 << 1 = 1101 0010
11010010 → 反码1101 0001 → 原码1010 1110,真值为-46

3.3 有符号右移 >>

也称之为“算术右移运算符”。
运算规则:去低位,补高位。若是正数,补0;否则,补1(一直补)。

示例:
1、计算23 >> 1

23的补码:0001 0111
23 >> 1 = 0000 1011,真值为11

2、计算 -23 >> 1

-23的补码:1110 1001
-23 >> 1 = 1111 0100
1111 0100 → 反码1111 0011 → 原码1000 1100,真值为-12

3.4 无符号右移 >>>

也称之为“逻辑右移操作符”。
运算规则:去低位,补高位,无论正负,都补0

示例:
1、计算23 >>> 1

23的补码:0001 0111
23 >>> 1 = 0000 1011,真值为11

2、计算-23 >>> 1

-23的补码:11111111 11111111 11111111 11101001
-23 >>> 1 = 01111111 11111111 11111111 11110100,
真值为Integer.MAX_VALUE - 11 = 2147483636

从上文可知,负数补码的第一个1位前都是1

3.5 按位与 &

运算规则;当对应二进制位同为1时,得1。(不考虑正负)

示例:
1、计算7 & 23

7的补码:0000 0111
23的补码:0001 0111
7 & 2 = 0000 0111,真值为7

2、计算7 & -23

7的补码:0000 0111
-23的补码:1110 1001
7 & -23 = 0000 0001,真值为1

3.6 按位或 |

运算规则:当对应二进制位有一个为1时,得1。(不考虑正负)

示例:
1、计算7 | 23

7的补码:0000 0111
23的补码:0001 0111
7 | 23 = 0001 0111,真值为23

2、计算7 | -23

7的补码:0000 0111
-23的补码:1110 1001
7 | -23 = 1110 1111,真值为-7

3.7 按位异或 ^

运算规则:当对应二进制位相同时得0,否则得1、(不考虑正负)

示例:
1、计算7 ^ 23

7的补码:0000 0111
23的补码:0001 0111
7 ^ 23 = 0001 0000,真值为16

2、计算7 ^ -23

7的补码:0000 0111
-23的补码:1110 1001
7 ^ -23 = 1110 1110,真值为-18

3.8 扩展

我在看Java-API源码时,遇到一些奇怪的位运算符,如:<<=|=,这些位运算符平日很少见,一查才知道,其实就是位运算后赋值,类似+=*=

示例见Integer类的第4.12项,其底层运用位运算|=获取最高位的 1 位的位置。

4、位运算运用示例

4.1 左移与算术右移的巧妙运用

先说结论:

<<的结果是原来的2倍>>的结果是原来的1/2(取整,即若是奇数,先-1,再取1/2)。

示例说明:
123 << 1 = 46
计算:
23 = 16 + 4 + 2 + 1 = 24 + 22 + 21 + 200001 0111
23 << 1 = 0010 1110 → 25 + 23 + 22 + 21 = 2*(24 + 22 + 21 + 20)。
——————
每1位都左移1位,变成原来的2倍,故总和也是原来的2倍。

223 >> 1 = 11
计算:
23 >> 1 = 0000 1011→ 23 + 21 + 20 = (24 + 22 + 21 + 20)/2。
——————
当然,这个等式是不成立的,因为等号右边多了个 20,即多了1。不过,大家肯定已经看出来了我这么写的用意。
23是奇数,它的二进制是0001 0111,最低位是1。右移1位,这个 1/20 就没了,等式成立;而其他位都变为原来的1/2。因此,总和也变成原来的1/2

3-11 >> 1 = -6

计算-11的补码:-11原码1000 1011 → 反码1111 0100 → 补码1111 0101
-11 >> 1 = 1111 1010
1111 1010 → 反码1111 1001 → 原码1000 0110,真值为-6

-11是奇数,同样先-1,再取1/2

4-6 << 1 = -12

计算-6的补码:-6原码1000 0110 → 反码1111 1001 → 补码1111 1010
-6 << 1 = 1111 0100
1111 0100 → 反码1111 0011 → 原码1000 1100,真值为-12

之所以计算得这么详细,是为了方便大家观察>>/<<两种位运算的规律,即结果相反。

补充说明
其实负数的位运算不必计算其补码(纯属个人习惯)。
示例:

-10的原码:1000 1010
-10 >> 1 = 1000 0101,真值为-5	// 注意:这里右移补的是0

-5 << 1 = 1000 1010,真值为-10

哈哈。。。其实此技巧也不绝对。通过此方法进行计算,比如:-11 >> 1 = -5-15 >> 1 = -7,两个结果显然不对。当然,我还做了其他测试,负奇数和负偶数都有,结果有正确的也有错误的。
由于这只是我发现的一个规律,并没有理论支撑,所以未列举出来进行说明。不过,似乎负偶数使用此技巧运算的结果是对的(也算是一个规律吧。。。)。
因此,大家对这个技巧有个印象就行,实际计算还是要严谨。

结语:
为什么说上文这个性质巧妙?
因为我发现在很多源码中都采用这种方法进行数值翻倍或取半,特别是一些包装类(java.lang.*)或工具类(java.util.*)。
例如:关于二进制的原码、补码和反码,以及表示范围、常见位运算符和进制转换的理解与简述

这是java.util.ArrayList<E>类的扩容方法,很经典的例子,方法详情见源博文的第5.6项。

4.2 实现字符大小写转换

char 类型对应ASCLL码,对字符进行-/+ 32运算即可实现大小写转换。

在查阅关于位运算的资料时,我发现通过位运算也可以实现字符大小写转换。由于位运算的对象是二进制,故效率优于算术运算。好奇测试一下发现,如果都运算一亿次,时间差在几十甚至几微秒之间,实际差距微乎其微。因此,我将此方法记录下来的主要目的是为了“扩展思维”。

看下述示例。
1'A' ^= 32 → 'a'

计算字符'A'的补码:'A' = 65 = 0100 0001
`'A' ^ 32 = 0100 0001 ^ 0010 0000 = 0110 0001,真值为97

2'a' ^= 32 → 'A'

'a'的补码:0110 0001
'a' ^ 32 = 0110 0001 ^ 0010 0000 = 0100 0001,真值为65

3'A' |= 32 → 'a'

'A' | 32 = 0100 0001 | 0010 0000 = 0110 0001,真值为97

4'a' &= -33 → 'A'

计算-33的补码:-33的原码1010 0001 → 反码1101 1110 → 补码1101 1111
'a' & -33 = 0110 0001 & 1101 1111 = 0100 0001,真值为65

4.3 生成 IP 地址

大家看一个例子就明白了,看这里 → Inet4Address类的第2.2项,这里就不再赘述。

5、关于进制间转换

  1. 八进制以0(零)开头,十六进制以0x(零)开头;
  2. 八进制以0 ~ 7这8个数表示,十六进制以0 ~ 9 a ~ f这10个数和5个字母表示;
  3. 十六进制中的aA相同;
  4. 其他进制<- ->十进制、二进制<- ->十进制的运算方式,两者相同;(<- ->表示相互转换)
  5. 十六进制->二进制的方法:将每一位转换成4位二进制,然后合并(注:4位二进制最大值为1111,得15;十六进制的最大值为f,即15)。
    示例:1、0xa1010的二进制是0000 1010;2、0x14201的二进制是00014的二进制是0100,合并得0001 0100,即20

最后

本文中的例子为了阐述这七种常见位运算符的运算步骤、方便大家理解而简单举例的,23-23是任意取的数,没有特别意义。

PS:
单纯的位运算最大的作用就是帮助我们掌握位运算的基础,没有太大实用价值。大家可以偶尔去看看一些源码中对位运算的运用,很多真的很巧妙,而且还能查漏补缺。
本文能有如此规模,得益于我对二进制、位运算的理解,以及平日解析源码时频繁运用位运算。

本文完结。文章来源地址https://www.toymoban.com/news/detail-787612.html

到了这里,关于关于二进制的原码、补码和反码,以及表示范围、常见位运算符和进制转换的理解与简述的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • verilog学习笔记5——进制和码制、原码/反码/补码

    2023.8.13 天气晴 整数:除以2,余数倒着写 小数:乘以2,正着写 例题1 : 例题2 : 十进制数13.613转化为二进制数,要求误差小于1% 乘以2的次方 乘法 :被乘数左移,后相加 除法 :除数右移,被除数/余数去减去除数 8位二进制数的范围: 有符号数 无符号数 -128~127 0~255 原码 反

    2024年02月13日
    浏览(33)
  • 位运算(按位与、按位或、异或、取反)以及原码、反码、补码

    目录 位运算 按位与运算符 [ ] 按位或运算符 [ | ] 异或运算符 [ ^ ] 取反运算符 [ ~ ] 移位操作 一些面试常考的位操作运算 获取二进制中最右边的1 计算机原码、反码、补码 机器数 “三码”之间的转换 计算机中为啥要用补码呢? 真数 原码 反码 补码 有了原码为什么要使用反码

    2024年02月02日
    浏览(34)
  • C语言【进制转换】35:输出二进制补码

    总时间限制:  1000ms 内存限制:  65536kB 描述 输入一个整型(int)的整数,输出它的32位二进制补码。 输入 一个整型整数。 输出 输出一行,即该整数的补码表示。 样例输入 样例输出 00000000000000000000000000000111 代码实现: 首先要明白 (按位与)和 (左移)的用法 规则: 11=1 10=

    2024年02月07日
    浏览(48)
  • java~二进制补码的用途

    二进制补码(Binary Two\\\'s Complement)是一种表示有符号整数的方法,在计算机中广泛使用。它是通过对正数取反加一得到负数的表示方式。 在二进制补码表示中,一个固定位数的整数由固定数量的二进制位表示,其中最高位被用作符号位。对于N位的二进制补码表示,最高位(最

    2024年02月09日
    浏览(25)
  • 【Golang】补码二进制字符串转整型

     在计算机科学中,原码、反码和补码是用来表示有符号整数的三种不同的二进制编码方式。下面将详细解释每一种编码方式的特点和用途。 原码  原码是最直观的有符号数表示方法。在原码表示法中,最高位(符号位)用来表示数的正负,0表示正数,1表示负数。正数的原

    2024年01月21日
    浏览(45)
  • 原码,反码,补码

            原码、反码和补码是计算机中表示有符号整数的三种不同的二进制表示形式。它们的理解和原理涉及了计算机内部整数表示的数学概念和编码规则。 十进制数 原码 反码 补码 +5 00000101 00000101 00000101 +4 00000100 00000100 00000100 +3 00000011 00000011 00000011 +2 00000010 00000010 0000001

    2024年02月15日
    浏览(30)
  • 原码,反码,补码,移码

    原码、反码、补码和移码是在计算机中表示有符号整数的方法。它们是为了处理正负数的运算和表示而设计的。下面我会逐个解释这些概念,并说明它们的使用和理解。 原码(Sign-Magnitude Representation): 原码是最简单的表示方法,其中最高位表示符号(0代表正数,1代表负数

    2024年02月08日
    浏览(30)
  • 什么是原码、反码和补码

    前言 一个数在计算机中的表示形式是二进制的话,这个数其实就叫机器数。 机器数通常是带有符号的(指有正数和负数之分),计算机用最高位存放符号,这个 bit 一般叫做符号位。 正数的符号位为 0, 负数的符号位为 1。比如,十进制中的数 +7 ,计算机字长为8位,转换成

    2023年04月09日
    浏览(25)
  • 原码、补码、反码的关系及应用场景

    是三种表示有符号整数的方法,它们之间存在一定的关系。 原码 是最基本的表示方法,即将一个数的符号位和数值位分开表示,符号位用0表示正数,用1表示负数。例如,+7的原码为00000111,-7的原码为10000111。 反码 是在原码的基础上,将负数的数值位按位取反得到的表示方

    2024年02月06日
    浏览(35)
  • [ARM汇编]计算机原理与数制基础—1.1.3 二进制补码

    在计算机中,为了表示有符号整数(即正数和负数),通常采用二进制补码表示法。二进制补码不仅可以表示负数,还能简化计算机的加法和减法运算。接下来,我们将介绍二进制补码的概念及其计算方法。 原码、反码和补码 在讨论补码之前,我们先了解一下原码和反码的

    2024年02月08日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包