计算机是怎么存储数字的

从一个最简单的例子开始吧。

我们来做几个小学生的加法题。8 + 9 等于多少?0.8 + 0.9 等于多少?在 Chrome 里面简单用 Javascript 这门语言简单测试一下,我们得到的结果是:
8 + 9 = 17
0.8 + 0.9 = 1.7000000000000002。
是的,你没有看错,计算机给出了 1.7000000000000002 这个答案。难道计算机错了吗?

计算机错了,又没错。为什么呢?因为计算机是用二进制来表示这些数字的,有很多数在计算机里面是没法精确表示的。先来看看什么是二进制。

十进制、二进制

我们都知道,计算机只能懂 0 和 1,也就是说计算机里面所有的数字,都是二进制形式的。那么一个普通的数字,用二进制的形式究竟该如何表示呢?譬如:

  • 78 如何使用二进制表示?

对于正整数,数学上有一个转换的公式,就是「除 2 取余,逆序排列」法。具体做法是:用 2 整除十进制整数,可以得到一个商和余数;再用 2 去除商,又会得到一个商和余数,如此进行,直到商为 0 时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。对于 78 这个十进制整数来说,对应的二进制是 1001110.

  • 0.5 如何使用二进制表示?

对于纯小数,也有一个固定的转换法:乘 2 取整,顺序排列。具体做法就是:用 2 乘十进制小数,可以得到积,将积的整数部分取出,再用 2 乘余下的小数部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,此时 0 或 1 为二进制的最后一位。对于十进制的 0.5 来说,对应的二进制表示就是 0.1

  • 8.5 如何使用二进制表示?

好了,有零有整的小数,在计算机里面怎么表示呢?可能大家已经知道了,分成整数和纯小数两部分,分别转换,然后相加起来。那么 8.5 的二进制形式可以表示为:1000.1

好了,这是基础,后面的内容就开始慢慢接近计算机的工作模式了。

二进制能准确表示十进制数字吗

首先看这个问题:

  • 0.1 这个小数,用二进制该如何表示

按照前面的转换规则,我们先看看看 0.1 怎么表示:
0.1 * 2 = 0.2 ---- 0 (1)
0.2 * 2 = 0.4 ---- 0 (2)
0.4 * 2 = 0.8 ---- 0 (3)
0.8 * 2 = 1.6 ---- 1 (4)
0.6 * 2 = 1.2 ---- 1 (5)
0.2 * 2 = 0.4 ---- 0 (6)

算到这里,我们得到了 0.000110 这样的一段二进制串,但是不能再算下去了,因为我们看到第 6 行和第 2 行完全一样,再计算就一直循环下去了。所以,0.1 这样一个十进制小数,在计算机的二进制里面根本没法精确表示。

思考一下,0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 这 9 个小数,有几个是可以精确表示的?

  • 精确表示 Vs 精确显示

通过前面的代码我们知道,0.8 + 0.9 的结果,计算机并不能精确表示,但是我们在 Javascript 中定义这样一个数值变量

var ha = 1.7

在控制台打印的话,结果又确实是 1.7。

好像很矛盾。显然,通过后面的例子可以知道 Javascript 可以精确显示 1.7(哪怕 1.7 不能被精确表示),这不是和最开始我们的结果矛盾吗?

其实这是一个想当然的错误。在直观上我们认为 0.8 + 0.9 = 1.7 是必然成立的(数学上确实如此),而且 1.7 又能被精确显示,那最开始的结果就应该等于 1.7,对不对?

而实际上,在计算机里 0.8 + 0.9 根本不等于 1.7(什么?)!因为 0.8 和 0.9 都不能被精确表示,数值的精度丢失在了每一个环节,而不是最后的结果上。

我们可以用数学中四舍五入的概念类比一下。我们计算 1.6 + 2.8 保留整数,我们觉得结果应该是 4(4.4 舍入)。但是我们用另一种方法:先把 1.6 舍入成 2,再把 2.8 舍入成 3,最后得到 5。通过不同的运算,我们得到了完全不一样的结果!所以,在 0.8 + 0.9 的运算中,参与运算的两个数精度一开始已经丢失了,所以他们的结果也不再是 1.7 了。

看到这里,我们是不是要怀疑计算机的一切结果了?连几个小数都无法精确表示的话,其他数值计算结果如何保证准确呢?其实,大家不用担心,计算机在做浮点运算的时候,常常会因为无法精确表示而进行近似或舍入,而其结果在我们日常生活所需的精度范围内,都还是准确的。

刚才说到了浮点运算,照字面的理解就是浮点数的运算了。但是,浮点数是什么呢?计算机里面是怎么表示这些数字的呢?

浮点数是什么

为了弄清楚浮点数,我们需要先看定点数。所谓定点数,是计算机中采用的一种数的表示方法,参与运算的数的小数点位置固定不变。那么小数点位置可变的自然就是所谓的「浮点数」了。这里要先澄清一个概念:浮点数并不一定等于小数,定点数也并不一定就是整数;整数和小数是我们小学数学中使用的,在计算机的世界里,只有定点数和浮点数。

  • 定点整数

小数点位固定在最后一位之后称为定点整数。若机器字长为 8 位,那么它能表示的整数范围是 -127 - 127(考虑正负数的符号)。例如 0111 表示 7。

  • 定点小数

小数点固定在最高位之后称为定点小数。若机器字长为 8 位,数值表示范围是[-(1-2^(-7)), 1-2^(-7)]。例如 1111 表示 -0.875。

  • 浮点数

在计算机的硬件中是没有小数点这个东西的。CPU 能处理的东西都是整的。所以,小数就要用类似科学计数法的方式来表示。如 1.234 在计算机里面,可以理解成用 1234 和 -3 两个整数来表示:1234 * 10 的 -3 次方,这类数就叫「浮点数」。当然,计算机里面的浮点数是二进制的。任意一个二进制浮点数 V 都可以表示成下面的形式:

V = (-1)^s x M x 2^E

这里:

  1. (-1)^s 表示符号位,当s = 0,V 为正数;当 s = 1,V 为负数;
  2. M 表示有效数字,必须大于等于 1、小于 2;
  3. 2^E 表示指数位。

举例来说,十进制的 5.0,写成二进制是 101.0,相当于 1.01 x 2^2。那么按照上面的格式,可以得出 s = 0, M = 1.01, E = 2。

CPU 在处理这类数的运算时需要比整数运算复杂得多的电路设计,且速度比整数运算慢很多(所以很多年前,浮点运算能力是衡量 CPU 性能的重要指标)。

浮点数的更多问题

历史上计算机科学家们曾提出过多种解决方案,最终获得广泛应用的是 IEEE 754 标准中的方案。IEEE 754 规定,对于 32 位的浮点数,最高的 1 位是符号位 s,接着的 8 位是指数 E,剩下的 23 位为有效数字 M:

32 位浮点数的分段表示

IEEE 754 对有效数字 M 和指数 E,还有一些特别规定。

前面说过,1≤M<2,也就是说,M 可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1,因此可以被舍去,只保存后面的 xxxxxx 部分。比如保存 1.01 的时候,只保存 01,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。以 32 位浮点数为例,留给 M 只有 23 位,将第一位的 1 舍去以后,等于可以保存 24 位有效数字。

至于指数 E,情况就比较复杂。

首先,E 是没有符号的。这意味着,如果 E 为 8 位,它的取值范围为 0~255;但是,我们知道,科学计数法中的 E 是可以出现负数的,所以 IEEE 754 规定,E 的真实值必须再减去一个中间数(偏移值)。IEEE 754 标准规定该固定值为 2^(E-1) - 1,对于 8 位的 E,这个中间数是 127。

比如,2^10 的 E 是 10,所以保存成 32 位浮点数时,必须保存成10+127=137,即 10001001。

然后,指数 E 还可以再分成三种情况:

  1. E不全为 0 或不全为 1。这时,浮点数就采用上面的规则表示,即指数 E 的计算值减去 127,得到真实值,再将有效数字 M 前加上第一位的 1;
  2. E全为 0。这时,浮点数的指数 E 等于 1-127,有效数字 M 不再加上第一位的 1,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ±0,以及接近于 0 的很小的数字;
  3. E 全为 1。这时如果有效数字 M 全为0,表示 ±无穷大(正负取决于符号位s);如果有效数字M不全为0,表示这个数不是一个数(NaN)

浮点数表示范围与表示个数
对于计算机来说,浮点数可表示的范围相当大,但这并不等于表示个数。从数量级分析一下,32bit 浮点数的表示范围是 10 的 38 次方,而表示个数呢,是 10 的 10 次方。 能够被表示的数只有 1/100000000…. (大概有30个零)。

总之,计算机中数字的存储、表示、计算是非常复杂的,涉及到的内容,都够单独出一本大部头的书了,大家有兴趣可以自己找资料深入研究一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容