计算机中带符号的整数为何采用二进制的补码进行存储?

本贴最后更新于 2528 天前,其中的信息可能已经物是人非

我们都知道在计算机内部数据的存储和运算都采用二进制,是因为计算机是由很多晶体管组成的,而晶体管只有 2 种状态,恰好可以用二进制的 0 和 1 表示,并且采用二进制可以使得计算机内部的运算规则简单,稳定性高。在计算机中存在实数和整数,而整数又分为无符号整数和有符号整数,无符号的整数表示很简单,直接采用其二进制形式表示即可,而对于有符号数的表示却成了问题,如何表示正负?如何去处理正负号?下面来具体说下其中的原因,在这之前先了解一下原码、反码和补码这几个概念。

1.原码、反码和补码的概念

  在了解原码、反码和补码之前先说一下有符号数和无符号数。用过 C 语言的都知道在 C 语言中用 signed 和 unsigned 来标识一个数是否是有符号还是无符号类型的。对于一个 8bit 的二进制来说,若当做无符号数处理,其能表示的整型值范围是 0255,但是这样表示数据就有个局限性,如果数据是负的该如何表示?因此就引入了有符号类型的概念,对于有符号类型,规定取最高位为符号位,若最高位为 0,则为正数,否则为负数,这样一来对于 8 位二进制,示数值的就只有 7 位了,能够表示的非负数值范围变为 0127,负值范围为-127-1,相当于可以理解为将无符号类型能够表示的 128255 拿来去表示-127~-1 了。事实上,在计算机内部存储中,计算机自己是无法去区分无符号还是有符号类型的,对于 255 和-1,在计算机内部存储的都是 11111111。换个角度来说,如果事先知道内存中存储了这样一个 8 位二进制 11111111,但是谁也不能肯定它具体表示什么数值,是-1 还是 255?这个是需要靠程序员自己去指定的,如果指定为无符号类型,则编译器则通过相应指令将其转换为数值 255。事实上对于-x 的二进制补码表示形式和(256-x)(256-x 当做无符号类型处理)的二进制表示形式相同,从这里可以略微了解了补码的含义了。在教材中对于原码、反码以及补码一般是这么定义的:

  对于正数原码、反码以及补码是其本身。负数的原码是其本身,反码是对原码除符号位之外的各位取反,补码则是反码加 1。

  因为(-x)的二进制补码形式和 256-x 的二进制表示形式相同,而 255-x 相当于对 x 的每一位取反,那么 256-x 就是 255-x 后加 1。

  注意:1)原码、反码、补码的概念是针对有符号类型而言的。

     2)实数始终是有符号类型的(实数并不是采用补码形式存储的,具体可参考《浅谈 C/C++ 的浮点数在内存中的存储方式》一文),整型数据包括无符号和有符号类型的。

2.采用补码表示带符号的整数的原因

  对于有符号类型的整数,有原码、反码和补码三种形式,最后选择了补码来表示,具体来说有下面几点原因。

  1)能够统一 +0 和-0 的表示

  采用原码表示,+0 的二进制表示形式为 0 000 0000,而-0 的二进制表示形式为 1 000 0000;

  采用反码表示,+0 的二进制表示形式为 0 000 0000,而-0 的二进制表示形式为 1 111 1111;

  采用补码表示,+0 的二进制表示形式为 0 000 0000,而-0 的二进制表示形式为 1 111 1111+1=1 0000 0000,因为计算机会进行截断,只取低 8 位,所以-0 的补码表示形式为 0000 0000。

  从上面可以看出只有用补码表示,+0 和-0 的表示形式才一致。正因为如此,所以补码的表示范围比原码和反码表示的范围都要大,用补码能够表示的范围为-128127,0127 分别用 0000000001111111 来表示,而-127-1 则用 10000001~11111111 来表示,多出的 10000000 则用来表示-128。因此对于任何一个 n 位的二进制,假若表示带符号的整数,其表示范围为-2^(n-1)~2^(n-1)-1,且有 MAX+1=MIN。看下面一段代码:

char ch=127;
ch++;

  ch 的值是多少?它的值是-128,读者可以上机验证一下。

  假如不采用补码来表示,那么计算机中需要对 +0 和-0 区别对待,显然这个对于设计来说要增加难度,而且不符合运算规则。

  2)对于有符号整数的运算能够把符号位同数值位为一起处理

  由于将最高位作为符号位处理,不具有实际的数值意义,那么如何在进行运算时处理这个符号位?如果单独把符号位进行处理,显然又会增加电子器件的设计难度和 CPU 指令设计的难度,但是采用补码能够很好地解决这个问题。下面举例说明:

  比如-2+3=1

  如果采用原码表示(把符号位同数值位一起处理):

  1 000 0010+0 000 0011=1 000 0101=(-5)原,显然这个结果是错误的。

  如果采用反码表示

  1 111 1101+0 000 0011=1 0000 0000=0 0000000=(+0)反,显然这个结果也是错误的。

  如果采用补码表示

  1 111 1110+0 000 0011=1 0000 0001=0000 0001=(1)补,结果是正确的。

  从上面可以看出,当把符号位同数值位一起进行处理时,只有补码的运算才是正确的。如果不把符号位和数值位一起处理,会给 CPU 指令的设计带来很大的困难,如果把符号位单独考虑的话,CPU 指令还要特意对最高位进行判断,这个对于计算机的最底层实现来说是很困难的。

  3)能够简化运算规则

  对于-2+3=1 这个例子来说,可以看作是 3-2=1,也即[3]+[-2]=1,从上面的运算过程可知采用补码运算相当于是

  [3]补 +[-2]补=[1]补,也即可以把减法运算转换为加法运算。这样一来的好处是在设计电子器件时,只需要设计加法器即可,不需要单独再设计减法器。

  总的来说,采用补码主要有以上几点好处,从而使得计算机从硬件设计上更加简单以及简化 CPU 指令的设计。

测试代码

#include

int main(void)
{ char ch=-1; char *p=(char *)&ch;
unsigned char uch=*p;
printf("%d\n",uch); //输出结果为 255
return 0;
}

Why int32 has max value 2^31 -1

Int32.MaxValue = 2^31 - 1 = 01111111111111111111111111111111 1 = 00000000000000000000000000000001 0 = 00000000000000000000000000000000 -1 = 11111111111111111111111111111111 Int32.MinValue = -2^31 = 10000000000000000000000000000000

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    32 引用 • 108 回帖 • 1 关注
  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    125 引用 • 74 回帖 • 1 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 679 关注
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1742 回帖 • 6 关注
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 110 关注
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 8 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 367 回帖
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    591 引用 • 3528 回帖 • 1 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 2 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 3 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    947 引用 • 1460 回帖
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖
  • sts
    2 引用 • 2 回帖 • 243 关注
  • Word
    13 引用 • 41 回帖 • 2 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    172 引用 • 1538 回帖
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 564 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖
  • CodeMirror
    2 引用 • 17 回帖 • 172 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1444 引用 • 10083 回帖 • 506 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    181 引用 • 400 回帖 • 1 关注
  • SMTP

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。

    4 引用 • 18 回帖 • 636 关注
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1432 回帖
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    46 引用 • 114 回帖 • 169 关注
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    85 引用 • 324 回帖
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 196 关注
  • 反馈

    Communication channel for makers and users.

    120 引用 • 906 回帖 • 280 关注