深入理解计算机系统(2.3)---整数的表示方式精解》无符号与补码编码(重要)...

2023-11-18

           上一章我们简单的介绍了布尔代数以及C语言的位运算,本次我们主要来看,二进制如何表示整数,这是很重要的一章,希望各位猿友莫要错过。

 

C语言中的整数类型及范围

 

           我们依然以C语言为例,C语言当中提供了多种整数类型,一共十种,位数为1、2、4、8,其中32位机器上,4位的有两种,在64位机器上,8位的有两种。具体的LZ这里就不多做介绍了。以下是32位和64位系统上,这十种整数的范围。

            上述是C语言中各个整数类型的表示范围,不过C语言有它的最小数值范围,也就是说C语言要求这些数据类型至少要满足一个标准的范围。下图是C语言对整数类型要求的最小表示范围。

       

            仔细看的话,可以发现,C语言只要求有符号数的范围是对称的,另外就是int和long类型的位数要求都比较低,分别是2位和4位。

 

无符号编码

 

            可以看到以上的表中,每一种整数类型都可以加unsigned关键字,来表示一个无符号数,即没有正负之分。在书中,给出了无符号数的定义,LZ将它简化一下,对于一个w位的二进制来说,它的无符号表示为以下形式。

                                                      

             对于一个无符号编码来说,它的最大值和最小值很好确定,对于一个w位的二进制序列来说,当所有位都为0时,则为最小值,即

                                                        UMinw = 0

             而当所有位都为1时,则为最大值,根据等比数列的求和公式,即

                                                        UMaxw = 1 * (1-2w) / 1 - 2 = 2w - 1 

             如果把上述的定义看成是一个函数的话,那么这个函数就是一个双射。也就是说,对于任意整数x,当0 =< x < 2w的时候,存在唯一的二进制序列与其对应。反过来也是一样的,对于任意一个w位的二进制序列,都存在唯一一个整数x满足0 =< x < 2w,与这个二进制序列对应。

             无符号编码属于相对较简单的格式,因为它符合我们的惯性思维,上述定义其实就是对二进制转化为十进制的公式而已,只不过在一向严格的数学领域来说,是要给予明确的含义的。

 

补码编码

 

             无符号编码符合人的惯性思维是没错,但是可惜的是,它无法表示负整数。因此我们需要一种能够表示负数的整数表示方式,这就是补码编码。与无符号编码一样,书中依然给出了补码编码的定义,即对于任意一个w位的二进制来说,它的补码表示为以下形式。

                                                 

             这里最高位xw-1为符号位,当它为1时,该公式得到的值为负数,当为0时,得到的则为正数。

             我们观察这个公式,不难看出,补码格式下,对于一个w位的二进制序列来说,当最高位为1,其余位全为0时,得到的就是补码格式的最小值,即

                                                     TMinw = -2w-1 

             而当最高位为0,其余位全为1时,得到的就是补码格式的最大值,根据等比数列的求和公式,即

                                                     TMaxw = 1 * (1 - 2w-1) / 1 - 2 = 2w-1-1

             与无符号编码一样,如果把上述的定义看成是一个函数的话,那么这个函数同样是一个双射。也就是说,对于任意整数x,当-2w-1 =< x < 2w-1的时候,存在唯一的二进制序列与其对应。反过来,对于任意一个w位的二进制序列,都存在唯一一个整数x满足-2w-1 =< x < 2w-1,与这个二进制序列对应。

             相对于无符号编码来说,补码编码与我们的惯性思维有些不同,因此直观的理解起来会有些别扭,不过作为一个程序猿,我们应该有一颗半机器的脑子,尽量去适应机器的习惯。

 

两种编码的转换

 

             在C语言当中,我们经常会使用强制类型转换,而在之前的章节中,LZ也提到过强制类型转换。强制类型转换不会改变二进制序列,但是会改变数据类型的大小以及解释方式,那么考虑相同整数类型的无符号编码和补码编码,数据类型的大小是没有任何变化的,变化的就是它们的解释方式。比如1000这个二进制序列,如果用无符号编码解释的话就是表示8,而若采用补码编码解释的话,则是表示-8。

             考虑到上面我们已经说过,无论是无符号编码还是补码编码,其映射方式都是双射,因此它们都一定存在逆映射。如果我们定义U2Bw(x)为B2Uw(x)的逆映射,则对于任意一个整数x,如果0 =< x < 2w,经过U2Bw(x)的计算之后,将得到唯一一个二进制序列。同样的,如果我们定义T2Bw(x)为B2Tw(x)的逆映射,则对于任意一个整数x,如果-2w-1 =< x < 2w-1,经过T2Bw(x)的计算之后,也将得到唯一一个二进制序列。

             可以很明显的看出,对于0到2w-1-1这个区间内的整数来说,两种编码得到的二进制序列是一样的。为了得到其它区间里的整数的映射关系,我们定义

                                              T2Uw(x) = B2Uw(T2Bw(x))

             这个函数代表的含义是补码编码转换为无符号编码的时候,先将补码编码转换为二进制序列,再将二进制序列转换为无符号编码,最终也就是补码编码转为无符号编码的计算。

             下面我们简单的推算一下上面的定义,究竟是如何转换的,也就是无符号编码与补码编码的关系。我们将上面无符号编码和补码编码的公式相减,

             即                              B2Uw(x) - B2Tw(x) = xw-12w-1 - (-xw-12w-1) = xw-12w 

             即                                           B2Uw(x) = xw-12w + B2Tw(x)

             此处我们令x为T2Bw(x),则          B2Uw(T2Bw(x)) = xw-12w + B2Tw(T2Bw(x)) = xw-12w + x

             即                                            T2Uw(x) = xw-12w + x

             此时考虑xw-1的情况,当xw-1为1时,也就是补码编码表示负数的时候,T2Uw(x)则为2w + x 。(LZ小提示:此时x为负数,也就是说2w + x < 2w

             若xw-1为0时,则补码编码为正数,此时T2Uw(x) = x 。

             综上可知,有下列式子成立

                                                

             从这个式子中可以很明显的看出,最终得到的无符号数范围为0 =< x < 2w

             下图为表示补码编码与无符号编码的对应关系,可以看出在0至2w-1-1之间,两者是相等的,而其余区间则不同。

                                                   

             相反,我们用同样的方式也可以证明从无符号编码到补码编码的公式,这一部分书中省略了,LZ这里还是写上来,以免有的猿友不知所云。我们依然将无符号编码和补码编码的公式相减

                                           B2Uw(u) - B2Tw(u) = uw-12w-1 - (-uw-12w-1) = uw-12w 

             即                                           B2Tw(u) = B2Uw(u) - uw-12w

             此时我们令u为U2Bw(u),则    B2Tw(U2Bw(u)) = B2Uw(U2Bw(u)) - uw-12w = u - uw-12w

             即                                           U2Tw(u) = u - uw-12w

             此时考虑uw-1的情况,当uw-1为0时,也就是无符号编码数值小于2w-1的时候,U2Tw(u)则为u 。

             若uw-1为1时,也就是无符号编码数值大于或等于2w-1的时候,此时U2Tw(u)= u - 2w。(LZ小提示:此时U2Tw(u)为负数,因为 u < 2w

             综上,我们可以得到无符号编码转换为补码编码的公式

                                                       

             同样的,在0至2w-1-1之间,两者依然是相等的,而其余区间则不同。

                                                      

            

小例子

 

             以上则是我们讨论的有符号编码与补码编码的内容,下面为了让大家更好的理解上面的公式的含义,LZ带大家写一个小程序,我们来直观的在两者之间做一下强制转换,看下得到的值是否满足我们上面的结论。

             为了简单起见,我们取w为8,也就是我们只采用char类型作为例子,否则位数太高的话,算起来比较麻烦。LZ本次采用32位WIN7系统下,VS2010作为例子的编译环境。

#include <stdio.h>

int main(){
    char t = 0xFF;
    unsigned char u = 0xFF;
    printf("t=%d u=%u\n",t,u);
    printf("t2u=%u u2t=%d\n",(unsigned char)t,(char)u);
}

             我们给了两个同样的二进制序列0xFF,都是8个1,我们分别用补码编码和无符号编码去解释它们,并在后面使用强制转换在补码编码和无符号编码之间互相转换,得到以下结果。

t=-1 u=255
t2u=255 u2t=-1

             这里LZ就不再写计算的过程了,这个计算并不难。各位猿友可以自己套用上面的(2.6)和(2.8)公式去计算,看看是否符合公式的规定。

 

本章小结

 

             本章主要介绍了二进制表示整数时采用的无符号编码以及补码编码,也算是至今为止最难的一章了,因为搀和到了证明,尽管这是很简单的证明。各位猿友若看不懂的话,可以去LZ的交流群询问,希望猿友们不要中途放弃。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解计算机系统(2.3)---整数的表示方式精解》无符号与补码编码(重要)... 的相关文章

  • C#中unsafe的使用

    1 unsafe在C 程序中的使用场合 实时应用 采用指针来提高性能 引用非 net DLL提供的如C 编写的外部函数 需要指针来传递该函数 调试 用以检测程序在运行过程中的内存使用状况 2 使用unsafe的利弊 好处是 性能和灵活性提高
  • C++知识积累:内存对齐理解

    为什么要进行内存对齐 这是因为CPU的读取总是对齐的 举个例子 假设CPU是32位的 那么CPU每次读取的4字节数据的首地址都是4的倍数 也就是说 内存中数据首地址为4的倍数时 CPU一次操作就可以完成数据读取 假设有一个int型四字节大小
  • 范围for语句

    C 新标准提供的范围for语句 这种语句遍历给定序列中个元素并对序列中每一个值执行某种操作 其语法形式是 for declaration expression statement 其中 expression 部分是一个对象 用于表示一个序列
  • SQL 查询指定行数的数据。

    今天遇到一个关于 查询指定行数的数据 的sql查询语句问题 突然发现以前没怎么接触过 刚才想起来了 赶紧看了下文档 又上网搜了下 有了下面的东西 不知道有没有什么地方不对 oracle 先看一下文档中关于any和all的例子 很不错噢 An
  • C语言pcre库的使用及验证IP地址的合法性

    PCRE是一个用C语言编写的正则表达式函数库 它十分易用 同时功能也很强大 性能超过了POSIX正则表达式库和一些经典的正则表达式库 在使用PCRE库时 首先肯定是需要安装pcre的 不过一般的系统都会有自带的PCRE库 不过如果想使用最新
  • std::nth_element bug引起的crash问题

    1 源码 auto less compare const MirroringGroup mg1 const MirroringGroup mg2 gt bool return mg1 usage lt mg2 usage std nth e
  • C++工程师复习题

    一 auto ptr 类使用必须满足下列限制 1 不要使用 auto ptr 对象保存指向静态分配对象的指针 2 不要使用两个 auto ptrs 对象指向同一对象 3 不要使用 auto ptr 对象保存指向动态分配数组的指针 4 不要将
  • c/c++入门教程 - 1.基础c/c++ - 1.0 Visual Studio 2019安装环境搭建

    推荐视频课程 https www bilibili com video BV1et411b73Z p 2 已投币三连 b站果然是个学习的网站 本来是想在linux环境下运行QT 于是先学了几个月linux嵌入式驱动开发 后来发现太底层了 与
  • JNA模拟复杂的C类型——Java映射char*、int*、float*、double*

    文章目录 引言 Java Native Type Conversions Java和C基本类型指针对应关系 Pointer的具体用法 引言 最近项目在用Java调用C写的一些三方库 没办法直接调 用Java封装一下C的接口 这就少不了要用到
  • 【C++】VS code如何配置使用C++(手把手教学)

    博 主 米码收割机 技 能 C Python语言 公众号 测试开发自动化 获取源码 商业合作 荣 誉 阿里云博客专家博主 51CTO技术博主 专 注 专注主流机器人 人工智能等相关领域的开发 测试技术 VS code如何配置使用C 手把手教
  • lua和测试(一)

    lua做为一门高级语言 在游戏产业运用到机会越来越多了 测试掌握几门脚本语言也有一定的重要性 以下对于lua组合输入做出一些引导 测试需要掌握的关于返回数值 主要用到布尔类 前言的指引 lua的语法比较简单和清晰 学过c语言的可以很好的掌握
  • Trace Function Enter, Exit and Leave

    http developer nokia com community wiki Trace Function Enter Exit and Leave
  • 模板的完全特例化和部分特例化

    介绍 完全特例化就是类型完全明确的版本 而部分特例化指的是 只知道是几个参数的函数而不知道参数的类型 或者是只知道是引用或者是指针类型 而不知道具体是char 还是 int 模板特例化实例1 template
  • 手把手教你如何写一个三子棋/N子棋的小游戏

    这里写目录标题 第一步 游戏进入界面 第二步 初始化棋盘 第三步 打印棋盘 第四步 玩家和电脑下棋 第五步 判断输赢 三子棋或者N子棋怎么写 让我们先来玩一把 再来看看怎么写 程序运行界面 1为玩游戏 2为清屏 0为退出游戏 我们选1 然后
  • Public Private Protect Inheritance and access specifiers

    In the previous lessons on inheritance we ve been making all of our data members public in order to simplify the example
  • visual studio 一直显示正在准备解决方案

    首先重启电脑 无法解决的情况下执行以下步骤 Kill Visual Studio Open Visual Studio without loading a solution Disable AnkhSvn as Source Control
  • 一个简单的参数帮助框架,c实现

    文章目录 具体实现如下 include
  • enable_shared_from_this使用介绍

    文章目录 enable shared from this定义 使用场合 源码实现 注意 enable shared from this定义 定义于头文件 template lt class T gt class enable shared
  • C++实现函数重载的原理

    一 函数重载的概念 C 中允许存在同名函数 但要求函数参数的类型 个数不同 这些同名函数就称为函数的重载 void func int a int b cout lt lt func int a int b lt lt endl void f
  • 在 Solaris 上,使用 gcc 编译的库与使用 cc 生成的库的使用方式是否相同?

    我目前正在尝试编译 libxml2在 Solaris 上 当我运行源代码提供的 configure 脚本时 会自动使用 gcc 和 g 编译器 但是 我想使用 cc 和 CC 编译器 所以我跑 configure CC cc CXX CC

随机推荐