原文:https://www.jianshu.com/p/3004e5999be4

解决了关于补码扩展位宽时符号位一直补最高位的疑问  看3.补码的本质

1、在计算机内,有符号数有3种表示法:原码、反码和补码

有符号数在计算机中存储,用数的最高位存放符号, 正数为0, 负数为1 例如:有符号数 1000 0011,其最高位1代表负,其真正数值是 -3,而不是形式值131(无符号数1000 0011转换成十进制等于131)

原码:

原码就是符号位加上真值的绝对值,即用第一个二进制位表示符号(正数该位为0,负数该位为1),其余位表示值。

反码:

正数的反码与其原码相同;

负数的反码是对其原码逐位取反,但符号位除外。

补码:

正数的补码就是其本身;

负数的补码是在其反码的基础上+1

原码反码补码的定义,举个例子如下:

[+1] = [0000 0001]原 = [0000 0001]反 = [0000 0001]补 [-1] = [1000 0001]原 = [1111 1110]反 = [1111 1111]补

2、计算机内,为何要使用补码

对于计算机而言,加减乘数是最基础的运算, 要设计的尽量简单。我们知道,根据运算法则,减去一个正数等于加上一个负数,即:1 - 1 = 1 + (-1) = 0,所以计算机内部可以只有加法而没有减法,这样计算机的设计就简单了,于是人们开始探索将符号位也参与运算,将减法用加法替代。

我们分别用 原码 反码 补码 来计算下1 - 1的结果:

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

我们可以看到,使用原码和反码都不能正确的进行1 - 1的减法运算。

补码的优点:

可将减法变为加法,省去减法器;

无符号数及带符号数的加法运算可以用同一电路完成;

使用补码,修复了原码中0的符号(有 [+0] [-0] 之分)以及存在两个编码(0000 0000 和 1000 0000)的问题,而且还能够多表示一个最低数。

在8位二进制中:

使用原码/反码表示的范围为[-127,+127],包含 [+0] [-0] 一共256个;

使用补码表示的范围为[-128,+127],0没有符号。

3、补码的本质

补码为什么能正确实现加法运算呢?我们先从补码的由来开始说起

补码的由来

负数,其实就是0减去这个数的绝对值,比如 -8 = 0 - 8。8的二进制是 0000 0100,-8就可以用下面的式子求出:

  0 0 0 0 0 0 0 0
- 0 0 0 0 1 0 0 0
-----------

因为[0000 0000](被减数)小于[0000 1000](减数),所以不够减。请回忆一下小学算术,如果被减数的某一位小于减数,我们怎么办?很简单,问上一位借1就可以了。

  1 0 0 0 0 0 0 0 0-  0 0 0 0 1 0 0 0
-----------1 1 1 1 1 0 0 0

进一步观察,可以发现1 0000 0000 = 1111 1111 + 1,所以上面的式子可以拆成两个:

  1 1 1 1 1 1 1 1
- 0 0 0 0 1 0 0 0
-----------各位取反1 1 1 1 0 1 1 1
+ 0 0 0 0 0 0 0 1
-----------加11 1 1 1 1 0 0 0

以上就是-8的补码的转换过程。

因此负数的补码也可以表示为:1111 1111 + 1 - 负数的绝对值

用补码的方式将减法转换为加法的正确性验证

已知:X、Y都为正整数,Z = X - Y = X + (-Y)
证明:Z = X的补码 + (-Y的补码)

X的补码 = X; // 正数的补码为其自身
-Y的补码 = (1111 1111 - Y) + 1; // 负数的补码为除符号位的其它位取反加1 Z = X的补码 + (-Y的补码) = X + (1111 1111 - Y) + 1 = X - Y + 1 0000 0000 = X - Y + 0000 0000 = X - Y // 1 0000 0000就相当于0000 0000(舍去了最高位) 

这就证明了,在正常的加法规则下,可以利用2的补码得到正数与负数相加的正确结果。换言之,计算机只要部署加法电路和补码电路,就可以完成所有整数的加法。

4、补码表示的溢出问题

同号数相加如果结果的符号位和两加数不同,既是溢出。

例如8bit的byte类型的表示范围为[-128,+127],那么+128、+129、-129、-130等超出范围的数该怎么表示呢?

 // 超上限 溢出
+128 = 127 + 1 = [0111 1111]补 + [0000 0001]补 = [1000 0000]补 = -128 +129 = 127 + 2 = [0111 1111]补 + [0000 0010]补 = [1000 0001]补 = [1111 1111]原 = -127 // 超下限 溢出 -129 = -128 + (-1) = [1000 0000]补 + [1111 1111]补 = [0111 1111]补 = 127 -130 = -128 + (-2) = [1000 0000]补 + [1111 1110]补 = [0111 1110]补 = 126 

通过上述计算我们可以看出,对于8bit的数据(一共2^8 = 256个):

  • 超上限的数 x = x - 256;
  • 超下限的数 x = x + 256;
  • 下限的相反数与下限相等;
  • 上限的相反数是上限直接取负值。

证明举例如下:

byte a = -128, b = (byte) 128, c = (byte) 129, d = (byte) 130; byte e = (byte) -129, f = (byte) -130; System.out.println(a == ((byte)-a)); // true System.out.println(b); // -128 System.out.println(c); // -127 System.out.println(d); // -126 System.out.println(e); // 127 System.out.println(f); // 126 

5、符号扩展与多重转型

下面的代码的输出是什么?能看懂结果么?

byte b = -1;
System.out.println((int)(char)b); // 65535 
我们先来聊聊符号扩展(Sign Extension)

符号扩展用于在数值类型转换时扩展二进制位的长度,以保证转换后的数值和原数值的符号(正或负)和大小相同,一般用于较窄的类型(如byte)向较宽的类型(如int)转换。扩展二进制位长度指的是,在原数值的二进制位左边补齐若干个符号位(0表示正,1表示负)

Java中整型字面量种类
  • 十进制方式,直接书写十进制数字
  • 八进制方式,格式以0打头,例如012表示十进制10
  • 十六进制方式,格式为0x打头,例如0xff表示十进制255

需要注意的是,在Java中012和0xff返回的都是int型数据,即长度是32位。

Java类型转换规则(出自《Java解惑》一书)

1、如果最初的数值类型是有符号的,那么就执行符号扩展;
2、char是无符号类型,不管它要被转换成什么类型,都执行零扩展;
3、如果目标类型的长度小于源类型的长度,则直接截取目标类型的长度。例如将int型转换成byte型,直接截取int型的右边8位。

解析“多重转型”问题
(int)(char)(byte) -1 
  • int(32位) -> byte(8位)
    -1是int型的字面量,编码结果为0xffff ffff,即32位全部置1。转换成byte类型时,直接截取最后8位,所以转换后的结果为0xff,对应的十进制值是-1。
  • byte(8位) -> char(16位)
    由于byte是有符号类型,所以在转换成char型(16位)时需要进行符号扩展,即在0xff左边连续补上8个1(1是0xff的符号位),结果是0xffff,对应的十进制数是65535(char是无符号类型)。
  • char(16位) -> int(32位)
    由于char是无符号类型,转换成int型时进行零扩展,即在0xffff左边连续补上16个0,结果是0x0000 ffff,对应的十进制数是65535

6、几个转型的例子

先看下面代码的输出:

byte b=-1;
System.out.println((int)(char)(b & 0xff)); // 255 

1、byte型数值b -> char型:
char c = (char)(b & 0xff); // 不希望有符号扩展
char c = (char)b; // 希望有符号扩展

  • 0xff是int型字面量(0x0000 00ff),(b & 0xff)的结果是32位的int类型,前24被强制置0,后8位保持不变,然后转换成char型时,直接截取后16位。这样不管b是正数还是负数,转换成char时,都相当于是在左边补上8个0,即进行零扩展而不是符号扩展。
  • byte类型(8位)的b扩展成char型(16位)时需要进行符号扩展。

2、char型数值c -> int型:
int i = c & 0xffff; // 不希望有符号扩展
int i = (short)c; // 希望有符号扩展

  • 0xffff是int型字面量(0x0000 ffff),所以在进行&操作之前,编译器会自动将c(16位)转型成int型(32位),即在c的二进制编码前添加16个0,然后再和0xffff进行&操作,所表达的意图是强制将前16位置0,后16位保持不变。
  • 首先将c转换成short类型,它和char是 等宽度的,并且是有符号类型,再将short类型转换成int类型时,会自动进行符号扩展,即如果short为负数,则在左边补上16个1,否则补上16个0。

最后,介绍一种简单的 2的补码对应十进制数 的记忆方式:

0000 0000 = 0
那么在 0 的基础上 -1 就是(想象成借位减法):
1111 1111 = -1
以此类推:
1111 1110 = -2
1111 1101 = -3
...

作者:DevWang
链接:https://www.jianshu.com/p/3004e5999be4
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://www.cnblogs.com/yishuad/p/10382212.html

【转载】你真的了解补码吗相关推荐

  1. [转载] 你真的会用 Java 中的三目运算符吗

    参考链接: Java中的按位运算符 转载:http://blog.jobbole.com/93511/ 写在前面: 三目运算符是我们经常在代码中使用的,a= (b==null?0:1); 这样一行代码 ...

  2. 补码乘法实验原理_你真的理解补码吗?

    小编导读 "在计算机系统中,数值一律用补码来表示." 教科书上虽然这么说,但是却没有告诉我们为什么,今天小编就带大家一探究竟! 计算机数值运算的基础硬件就是加法器,所以我们就从加法 ...

  3. [转载] 你真的知道什么是 Python“命名空间” 吗?

    参考链接: Python命名空间和范围 写在之前 命名空间,又名 namesapce,是在很多的编程语言中都会出现的术语,估计很多人都知道这个词,但是让你真的来说这是个什么,估计就歇菜了,所以我觉得 ...

  4. 各位同意转载博文的善意,是否被恶意利用?文章被转载了,该不该收钱?

    我在CSDN,承蒙管理员和各位朋友的帮助,目前写的文章点击量稍可,所以经常会有朋友来问文章能否被转载?在一开始,我在高兴之余就直接同意了,因为在我眼里,转载是个双赢的事情,而且出于相互尊重,转载者应该 ...

  5. 整数二进制补码的数学原理(two's complement)

    转载自整数二进制补码的数学原理(two's complement) ================================================================== ...

  6. 负数时的有符号整型和无符号整型的转换

    目录 一.补码 二.负数时的有符号整型和无符号整型的转换 三.关于无符号的笔试题 一.补码 有符号数在计算机中存储,用数的最高位存放符号, 正数为0, 负数为1 例如:有符号数 1000 0011,其 ...

  7. 计算机数值表示Integer

    计算机数值表示&Integer 原码 反码 补码 为什么在计算机中使用补码 将减法转换为加法的正确性证明 Integer Short,Long,Byte Double,Float 原文章 计算 ...

  8. Lombok插件如此不好?但我选择继续使用…

    最近发现几个大号都在转载一篇<Lombok是让你代码处于"亚健康"状态的真正元凶>的文章,特意仔细阅读了该文,文中的观点基本上都不敢苟同,个人还是会坚持使用Lombok ...

  9. C语言中无符号整型变量赋值负数,负数时的有符号整型和无符号整型的转换

    目录 一.补码 二.负数时的有符号整型和无符号整型的转换 三.关于无符号的笔试题 一.补码 有符号数在计算机中存储,用数的最高位存放符号, 正数为0, 负数为1 例如:有符号数 1000 0011,其 ...

最新文章

  1. pythreejs is needed for plotting with pythreejs backend
  2. 1_CUDA编程介绍(20181121)
  3. python工具包_python 工具包
  4. select下拉列表选中后,跳转新链接
  5. vue修改html片段的样式无效,vue 组件中添加样式不生效的解决方法
  6. 自定义控件(Task01)——可以设置属性的控件
  7. 合法练习黑客技术?这15个网站也许可以帮到你
  8. c++ 线程间通信方式
  9. 【前端】:我的第一个网页
  10. springboot集成Spring Security oauth2(八)
  11. MATLAB 数据显示格式
  12. 企业数字化转型:构建“感知—思考—响应—反馈优化”闭环
  13. vscode中文乱码
  14. 工具类:获取两个经纬度的距离(米)
  15. 商业计划书PPT模板
  16. Android开发视频教程汇总
  17. Charles破解和安装【破解APP抓包限制】Xposed+JustTrustMe关闭SSL证书验证
  18. 计算机软件的英文简称,计算机常见英文缩写.docx
  19. regester正则用法_Regester(正则表达式测试器)
  20. 《彩虹屁》快夸夸我!彩虹屁生成器

热门文章

  1. Codeforces 447C - DZY Loves Sequences
  2. Java生成XML文件与XML文件的写入
  3. 实例讲解Linux系统中硬链接与软链接的创建
  4. 如何使用其他文件中定义的类Python
  5. Java多线程之阻塞I/O如何中断
  6. Windows Server 2012 R2 WSUS-11:经典的客户端排错操作
  7. tortosiegit github
  8. Notepad++ 更改和定制主题
  9. Windows 7环境下安装PHP 5.2.17
  10. 基于Maven构建Web项目