计算机数字的表示

我们知道计算机底层都是使用二进制来操作的,但是生活中我们更习惯使用十进制来计算和表示. 这其中又有什么关系呢?所以在阅读此文章之前,我们先问自己一下两个问题:

1.我们生活中常用的数字如何在计算机中使用二进制表示

2.这些数如何在计算机中做数字运算。

一、常用进制

对于习惯使用十个手指头的人类来说,早已习惯使用数字1~10来表示我们生活的数字。 我们也称之为十进制数。

但现在使用过计算机的人来说都知道计算机底层采用的都是二进制数来表示,这是由于依赖于底层电器信号的限制我们只能模拟出低电频和(0)和高电频(1)的信号,自然使用数字01表示最为合适. 但这种表示方法对于人来说不易读而且表示冗长;所以,向上又使用了十六进制来表示,使其变得易读。

二进制

二进制,也就是由01组成,用来表示一个十进制5数如下:

0000 0101

二进制数是可以计算的,也可以做逻辑运算,如我们计算一个十进制53:

  0000 0101
+ 0000 0011    #逢二进一
= 0000 1000
十进制

这个掰一掰手指就知道了,就不多说了。

十六进制

十六进制 在数学上是一种逢16进1的进位制。用数字0~9和字母A~F表示。-- 百度百科

十六进制的转换也很简单,介绍下二进制和十进制分别转换为16进制。

二进制数中每四位组成一个十六进制数,如下:

  0011 1011 0111 1110
     3   B     7    E

十进制转十六进制只要除十六取模,和十进制转二进制类似:

4877÷16=304....13(D)
304÷16=19....0
19÷16=1....3
1÷16=0....1
---------------
130D

在C语言中,以0x或者0X开头的数字常量被认为是十六进制的值。

二、数字编码

我们知道现在计算机普遍使用二进制来表示,这是因为二进制能够工作的更好。对于二进制数来说,单独的讲某一个位没有太多的意义,但是可以将多个位组合起来,再加上某种解释,就可以用来表示我们日常生活中常用的数字。 计算机中常用表示的数又分为有符号数无符号数浮点数

无符号

无符号表示大于或者等于0的数字,是基于传统的二进制表示法进行编码。

对于一个十进制数来说,如果我们想表示999这个数,我们也可以写成9*10^2+9*10^1+9*10^0.对于用二进制来表示表示就更简单了,二进制的每一个位的取值只有两种情况0或者1. 对于一个8位的二进制10110011数表示如下:

$$
1*2^7+0*2^6+1*2^5+1*2^4+0*2^3+0*2^2+1*2^1+1*2^0   => \sum_{}^{i}*{2^i}
$$

如上,该数字表示十进制数为179,对于8位的二进制数我们能够表示的范围为0(00000000)~255(11111111).

无符号的二进制数表示一个很重要的特性,也就是对于任何介于0~2^w-1 之间的数值都有一个唯一w为的值编码。

有符号数

有符号数表示可以为正或者为负的数字,通常采用补码进行编码。 负数是有符号的数值,对于负数来说,我们最难的就是如何表示它的负号.先来看看什么是原码补码反码

原码

原码是计算机中数字的二进制定点表示法。数码序列中最高位表示符号位,符号0表示正数,符号1表示负数;其余位表示数值的大小。这样我们使用一个符号位的引用就可以表示正数、负数。对于一个8位二进制数来说,能够表示的范围就是-127(1111 1111)~127(0111 1111)。 但对于原码表示有一个致命的缺点,就是不能参加运算。例如:-1(1000 0001) + 1(0000 0001) = 0(1000 0010) 这个二进制数的实际数值是-2.这显然不对。

反码

如果一个数是正数,那么这个数的反码就是它本身。 负数的反码是在原码的基础上,符号位不变,数值位按位取反。这样对于上述-1+1等于-2的问题就解决了。0001 + 1110(-1的反码) = 1111(反码) 转换到对应的数为-0. 但这又带来了一个问题.即0这个数有两种表示法+0和-0。

补码

补码的表示也分为两种,正数的补码是其原码本身,负数的补码是在其反码基础上再加1。

例如:

【+7】补码 = 0000 0111

【-7】 补码 = 1111 1001

我们使用补码来计算时,如果丢弃最高位对的进位,正负相加的确是为0的。

至此,我们可以使用补码来表示我们生活中常用的正数、负数;当然,这是我们丢弃了最高位的代价来换取的。

定点数

从名字来看,我们直观上可以理解为小数点为定点。当然定点数也是用来表示小数的,采用的是BCD码来编码。BCD码将0~9中的每个数用4位二进制数来表示,这样对于32位就可以表示8个数字,表示的数字范围也就是0~999999.99之间的1亿个实数。

BCD编码也有很多用途,比如银行、超市这种需要用到小数记金额的情况就很合适,但是如果表示很小、或者很大的数就不是很合适了,比如地球到太阳的距离,一个氢原子的半径等等。而且这样编码非常浪费,对于32位整数我们可以表示将近40亿个数字,而BCD编码的数字只能表示1亿个数字。

浮点数

上面讨论了使用补码表示负数、正数。但是对于浮点数来说补码就无能为力了。 计算机中另一种表示小数的方法是采用了类似于科学计数法的方式来表示浮点数(还有一种方式叫做定点格式)。这个制定的规则由美国电气和电子工程师协会(IEEE,全称是Institute of Electrical and Electronics Engineers)制定的.浮点数的标准定义了两种基本的格式:以4个字节表示的单精度和以8个字节表示的双精度格式。

IEEE表示的浮点数格式为:

$$
(-1)^s * M * 2^E
$$

对于单精度的表示法这三个部分一共32位,也就是4字节. s占1位;M占23位;E占8位。在双精度浮点格式中这三部分一共占64位。s占1位;M占52位;E占11位。

  • 符号s决定了这个数是负数(s=1)还是正数(s=0),对于数值0的符号位解释做特殊处理。
  • 尾数M是一个二进制小数位数,只存储23位。
  • 阶码E 的作用是对浮点数加权,这个权重是2的E次幂. 对于有符号指数,

以上的表达式所表示的编码值又分为三种情况: 规格化的值非规格化的值特殊值

规格化的值

这种情况表示E的位数值既不全为0,也不全为1. 阶码会被认为一个有符号的数值,其数值要被减去一个2^(k-1)-1的偏置值(bias),对于单精度来说表达式也就改写为如下形式,偏置值得数值范围为(-126~+127).

$$
(-1)^s * 1.f * 2^(e-127)
$$

对于M来说,我们它的大小可以通过调整E的值来确保M在范围1<=M<2之间。那么我们可以定义尾数M为M=1+f;这是一种轻松获取额外精度位的技巧(既然第一位总是为1,那么我们就不需要显示的表示它);这种方式也叫做隐含以1开头的表示

非格化的值

当阶码为全0时,所表示的数就是非格式化形式,此时阶码值为E=1-bias(偏置值).而尾数M的数值也就等于f. 此种格式是有两种情况:

  • e=0 且f=0. 则该数为0.这种情况下32位都设置为0. 符号位s可以表示0或1。
  • e=0 且f!=0. 该数也是合法的

特殊值

当阶码全为1时,所表示的数是一个特殊值:

  • e=255 且f=0 这个数表示无穷大或者无穷小,这取决于符号位s的值。
  • e=255 且f!=0,该值被解释为“不是一个数”,通常使用NaN表示。

对于一个8格式浮点数,其中e为4位,f为3位的浮点数表示如下:

三、数字运算

以上,我们介绍了如何用二进制表示一个十进制的正数、负数、浮点数。可以发现,采用不同的编码方式所表示的数范围不同。介绍完了数字如何用二进制表示,我们来看看如果使用二进制做我们常见的算术运算。

注: 在里没有特殊表示的情况下都采用4位二进制数来运算。

无符号数加法

对于一个4位的无符号数,我们可以表示的数字范围是0~15. 如果我们取两个数x=11,y=14;那么这两个数字的和(25)并不能被4位的数字范围所表示. 而需要5位来表示,这也就产我们常说的产生了溢出.

对于一个w位的数字来说,两个数相加,我们可以得到如下结论:

对于x+y,如果两个数的和小于2^w所表示的最大的数时,其结果表示x+y本身,但是如果大于,就需要舍弃最高位,也就相当于从和中减去了2^w.

补码加法

对于补码的加法运算来说会产生三种情况,结果太小正常结果太大. 三种情况如下:

对于补码来说,两个数x+y的取值范围为-2^w~ 2^w-2, 当和大于最大值时,我们说发生了正溢出,此时我们要减去最大值;当和小于最小值时,我们说发生了负溢出,此时要加上最大值。

无符号乘法

两个无符号数的乘法产生的数值大小范围为0~(2^w-1)^2. 所表示的数可能需要2w为来表示. 但实际中我们一般只表示低w位代表的值.

补码乘法

两个有符号数的乘法取值范围为-2^(w-1) * (2^(w-1)-1) ~ (-2(w-1)) * (-2(w-1)).以一个四位为列,乘积所表示的范围为-56~86. 这个数也需要2w位.长我们也是将得到的数截断w位来实现的.这相当于将该值模上2^w,在将无符号数转换为补码.

> U2T是一个将无符号数转换为补码的一个函数。

移位

不管是无符号数还是有符号数都可以通过移位来实现一个乘或者除一个2的k次幂的数.这在C语言中也叫做左移和右移操作,例如对于4位的数值11(1011),当左移两位时得到数值(101100),这个数所表示的无符号十进制也是44,将其截断后得到的数值便是1100(数值为12=44 mod 16).

在大多数机器上,整数的乘法指令相当慢,需要10个或者更多时钟周期,而对于其他整数运算(加法、移位)等只需要1个时钟周期,为此,编译器会使用一项重要的优化功能,C语言编译器会试图使用移位、加法、减法等组合来消除很多整数乘以常数的情况。如x * 14, 编译器会将重写为(x<<3) + (x <<2) + (x <<1),将一个乘法替换为三个移位和两个加法。

四、总结

以上就是我们如何在计算机里面去表示一个有符号数、无符号数、浮点数,以及我们如何使用他们做常见的加减乘除运算。在实际中乘法、除法都是多指令周期的运算,在底层编译器中会使用移位、加法等组合来优化这样的指令计算。

作者简介

吴松,2018年7月加入凯京科技。任职Python开发工程师,负责凯京科技Devops平台开发和版本迭代以及运维领域的研究和探索。

转载于:https://my.oschina.net/keking/blog/3079698

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值