学习札记:数据存储

我们听前辈讲过,计算机只认识0和1;换句话说,计算机是只认“二进制”的。

为什么要使用二进制存储数据呢?

我们知道,万事万物都不是“非黑即白”的,但“非黑即白”的事物存不存在呢?

灯开,灯灭;电路通,电路断;
或者,我们规定某个事物它发生的界限,比如一个器件的电位与2.5V的比较,它的结果也就可以简化成:≥,<这两种结果。

实现这些效果与效果切换的方法非常简单,我们只需要一个开关。开关也只有两种正常的工作状态:开,关。

由于这种方法极度简单,实现的成本极其低廉,因此,用“非黑即白”的方法储存数据、进行计算就成为了很好的选择。

那么,有没有一种进制,它只有两个元素,能模拟“非黑即白”呢?似乎只有二进制是合适的:它只有0和1两个元素。用大量二进制位表示数据是可行的,而且只需要与位数相当的开关(晶体管),成本极低。

而且可以证明,数据管理最高效的方式是利用E进制(对,E就是自然对数的底数);但人类并没掌握怎样使用非整数进制,因此,直接使用E进制是不太可能了。苏联科学家试着研究了三进制的计算机,因为3-E<E-2。而最终还是二进制计算机取代了三进制计算机成为当今世界的不二之王,究其原因,还是因为二进制的实现实在是太简单了,而且运算起来也比三进制方便。而且2也是除3以外距离E最近的整数了,其数据管理效率不会很低。

以上,决定了二进制在数据存储中不可取代的地位。那么,怎样把我们日常用的十进制数字转化为二进制存储使用呢?

原码、补码、反码

十进制二进制相互转化的方法,每一本数字逻辑电路书上都有介绍,我们只关注转化完的二进制序列。

以59为例子,我们假定现在是一个8位计算机:
59(10) turn to (A)(2):
A=00111011;这一串序列就是二进制下59(10)的表述;
由于它是由原数直接“翻译”得到的,我们称之为“原码”。

那么若是负数怎样表示呢?由于负号的介入,使得我们必须找出一个东西表示负号;幸而符号也是“非黑即白”的,我们大可以取一个位置,用0表示正,用1表示负。最后人们选定了序列最高的那一位,用于专门储存符号位。

那么-59(10)turn to (B)(2):
B=10111011;这一串也是“原码”。

但是原码有它天生的缺陷,那就是它的运算十分混乱;且参考一本数电教材的提示:

原码的运算如下:
①符号位不参与运算,单独处理
②A、B均表示绝对值
符号规则:
同号相加,异号相减,符号取被加(减)数的符号;
同号相减,异号相加,符号取绝对值较大数的符号

可以见得,原码的运算十分混乱,至少也需要两套电路:加法、减法。而补码的出现,统一了加减运算为加运算,使得运算电路大大简化。

在介绍补码前,先介绍反码,一个在计算机数据存储中似乎“没有什么用”的码制:
反码分两种:
正数:和原码相同;
负数:原码除符号位以外,统统按位取反(0 to 1,1 to 0)。
比如,59的反码,就是原码,为00111011;
-59的反码,就是原码除最高位按位取反,为11000100;

补码也分两种:
正数:和原码相同;
负数:反码+1;
比如,59的补码,就是原码,为00111011;
-59的补码,就是反码+1,为11000101;

补码的好处是,所有的运算在补码下进行,都沿用加法的那一套规则。比如计算-59+13=-46,其中:
-59:补码11000101
13:补码00001101
-46:补码11010010
容易发现,前两个补码直接相加就是-46的补码,并不需要考虑符号分别运算。一个加法器足够实现所有运算,所以实际上:

计算机内整数的存储,实际上储存的是其补码。

大小端字节序

并非所有设备都按照一个顺序存储数据。比如对-59的存储,不同机器就有两种存储方法:
A:11 00 01 01
B:10 10 00 11
你会发现,两种存储方法把“-59”的二进制序列以两种顺序:正&负排列。

于是,有人给他们起了名字:第一种为“小端字节序”,第二种为“大端字节序”。参考它们的实际意义,即:

⼤端模式:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。
⼩端模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。

所以实际上我们可以设计一个程序,利用1(10)这一整数来判断机器的字节序;

由于1的表示为:00000001,
在大端字节序机器中为:10000000
在小端字节序机器中为:00000001

所以我们可以利用位运算的不同结果来判断。

浮点数的存储?

整数可以利用补码存储,那浮点数该怎样处理?

负数可以用符号位表示正负,那浮点数的浮点该怎样表示?

于是,IEEE754标准中定义了一个标准的浮点数存储法:

V = pow(-1,S)∗ M ∗ pow(2,E)
• (−1)^S 表示符号位,当S=0,V为正数;当S=1,V为负数
• M 表示有效数字,M∈[1,2)
• 2^E表示指数位

//注意:由于编辑器限制,在V的表示中,我引用了pow符号,pow(A,B)等价于A^B

比如十进制的58.625,二进制形式为111010.101,它等价于:
1.11010101*2^5。那么S=0,M=1.11010101,E=5.

现在,我可以把一块内存(比如32位)分成三个区域,用来表示一个浮点数:
第1位:用于存储S,决定浮点数的符号;
第2-9位:储存E,表示指数位;
第10-32位:储存M,表示“基底”。

由于规定了M∈[1,2),容易发现,每个M都是以“ 1. ”开头的,因此我们成功舍去了浮点和前面的1,实际上在M存储区,存储的是小数点后面的那些部分。

这样,58.625在计算机看来就是:
0 00000101 110101010000000000000000

就完成了对浮点数的存储。

但要注意,这样存储没法存储很精确的小数。另外,如果用printf强行打印它的整数形式,就会打印出一个巨大的整数。

但无论如何,不可否认,我们日常使用的浮点数利用这种方式存储,是高效且巧妙的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值