计算机是如何存储数字的

前言

int a=12;
int b=1.234;

问:a 和 b 在计算机中到底是如何存储的?

答:转为二进制?

问:转成二进制就直接存储了?

答:...

问:小数 b 如何转成二进制的?

答:...

我们日常更多的时候都在使用十进制的数,要知道我们的祖先可厉害了,应该很长一段时间都在使用 16 进制。譬如一个成语叫“半斤八两”,解释:半斤、八两轻重相等,比喻彼此不相上下。等等!半斤和八两怎么相等,对的,宋代一斤就是等于十六两,当然半斤等于八两。

都知道计算机使用的是二进制,只用 0 和 1 就能表示数字,但是计算机到底是如何解决数字存储的问题?下面分别讨论整数和小数的存储:

如何存储整数

假设我们现在有一个 8 位的操作系统,最高位是符号位,1 表示负数,0 表示正数,所以能表示的最大的整数区间为 11111111 ~ 01111111 => -127 ~ 127

但是在存储表达的时候会遇到一个问题,10000000 和 00000000 两个 0 的问题,+0 和 -0 应该也是相等的,这给电路设计上带来了很多麻烦和多余的计算规则。

十进制转成二进制

简单点的记忆就是:除 2 取余再倒序。

  • 12 / 2 = 6..0
  • 6 / 2 = 3..0
  • 3 / 2 = 1..1
  • 1 / 2 = 0..1

0011 倒序 即为 1100,转成八位即在前面补0,则结果为 0000_1100。

0000_1100 是 java8 中的写法,我觉得看起来比较舒服且前后连贯,_ 是为了看起来比较清楚人为添加的,后面沿用这种写法,0000_1100 = 00001100= 0000 1100
都是相等。

一个天才的设计师

看了很多的文档没有找到解决这个问题的作者是谁?不管是谁反正提出了补码这个概念,真的是有效的解决了这个问题,不仅解决了 0 这个问题还带来了一个更大的优点,后面补充。

正数的原码、反码、补码

正数原码 = 反码 = 补码

  • 12 的原码是 0000_1100
  • 12 的反码是 0000_1100
  • 12 的补码是 0000_1100

这里并没有废话,尽是科学。

负数的原码、反码、补码

反码 = 原码符号位不变其他位取反

补码 = 反码 + 1

  • -12 的原码是 1000_1100
  • -12 的反码是 1111_0011
  • -12 的补码是 1111_0100

0 的存储

+0

  • 原码:0000_0000
  • 反码:0000_0000
  • 补码:0000_0000

-0

  • 原码:1000_0000
  • 反码:1111_1111
  • 补码:0000_0000

这里应该把前面的问题都解决了吧,补码不仅解决了 +0 和 -0 的问题,还神奇的把符号位都去掉了,计算机在运行的过程中从而可以更简单的进行运算。

减法运算

为了效率,计算机底层计算的时候是没有减法运算,减法运算都转成加法运算。

12 - 12 => 12 + (-12) => 0000_1100 + 1111_0100 => 0000_0000

  • 0000_1100 + 1111_0100 = 0 + 0 = 0
  • 0000_1100 + 1111_0100 = 0 + 0 = 0
  • 0000_1100 + 1111_0100 = 1 + 1 = 0 进 1
  • 0000_1100 + 1111_0100 = 1+ 0 + 进1= 0 进 1
  • 0000_1100 + 1111_0100 = 0+ 1 + 进1= 0 进 1
  • 0000_1100 + 1111_0100 = 0+ 1 + 进1= 0 进 1
  • 0000_1100 + 1111_0100 = 0+ 1 + 进1= 0 进 1
  • 0000_1100 + 1111_0100 = 0+ 1 + 进1= 0 进 1

因为只能存储 8 位,最后一个进 1 爆掉就剩下 0000_0000 了。

这里都是以 8 位计算机进行举例,现在的 32 位或者 64 位计算机都是同理的。

如何存储浮点数

相信很多人小数转成二进制都是不知道如何运算的,更别谈存储了,下面我就娓娓道来。

文章刚开始编辑的时候将浮点数称为小数,由于发现这样不专业,其实小数不一定都是浮点数,在那个没有标准各自为政的早计算机时代其实还是有定点数,感兴趣可以看参考文档。

浮点数转成二进制

1.234 在计算机中转成二进制是按照是分整数部分和小数部分进行的,整数部分上面以前讲到,这里说下小数部分 0.234 为例:

简单点的记忆就是:乘 2 取整再顺序。

  • 0.234 * 2 = 0.468 => 整数部分为 0 => 取 0
  • 0.468 * 2 = 0.936 => 整数部分为 0 => 取 0
  • 0.936 * 2 = 1.872 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.872 * 2 = 1.744 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.744 * 2 = 1.488 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.488 * 2 = 0.976 => 整数部分为 0 => 取 0
  • 0.976 * 2 = 1.952 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.952 * 2 = 1.904 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.904 * 2 = 1.808 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.808 * 2 = 1.616 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.616 * 2 = 1.232 => 整数部分为 1 => 取 1 (并将整数部分抹去)
  • 0.232 * 2 = 0.464 => 整数部分为 0 => 取 0
  • ...

下面就不写下去了结果是 0.001110111110...,写的越长精度越高。

可见浮点数存储必将是一个头疼的问题,浮点数底层的逻辑肯定也是比整数更为复杂。

IEEE754 标准

电气电子工程师学会(英语:Institute of Electrical and Electronics Engineers)简称为 IEEE,IEEE754是专门规定浮点数该如何存储的一个标准,规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。

任意一个浮点数都可以表示为:

V = (-1)^s \times M \times 2^E

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

例子:V = 0.234(十进制) = 0.001110111110(二进制) = 1.110111110 * 2^-3 ,则 s=0 ,M= 1.110111110,E=-3。

IEEE754 规定:

  • 对于 32 位的浮点数,最高的 1 位是符号位 s,接着的 8 位是指数 E,剩下的 23 位为有效数字 M。

  • 对于 64 位的浮点数,最高的 1 位是符号位 s,接着的 11 位是指数 E,剩下的 52 位为有效数字 M。

IEEE754 还有一些特殊的规定:

针对 M

由于 1<= M <=2 ,所以 M 始终为 1.xxxx 形式,xxxx 表示小数,那些对于计算机底层严苛的设计师这时候又要将 1 这一位舍掉,只保留 xxxx 部分,这样的好处是可以多储存一位有效数字。

针对 E

由于 E 是一个 8 位的无符号存储,只能表示 0 ~ 255,现实中的指数还存在负数,所以规定:E必须再加上一个中间数,对于 8 位的 E,这个中间数是 127;对于 11 位的 E,这个中间数是 1023,这样就可以将指数的表达范围扩大到 -127 ~ +128-1023 ~ +1024

实际中的取值范围是 -126~+127,-127 和 128 被用作特殊值处理,双精度同理。

阮一峰的博客中写的是 减去一个中间数 应该是有误的。

举个例子,如果 E=17,则 17+127 =144 ,实际的存储指为 144。

针对 E 还有一些特殊值的情况

  1. 如果指数 E 是 0 并且尾数的小数部分是 0,这个数是 ±0(和符号位相关)。
  2. 如果指数 E 是 1 并且尾数的小数部分是0,这个数是±∞(同样和符号位相关)
  3. 如果指数 E 是 1 并且尾数的小数部分非0,这个数表示为不是一个数(NaN)。

计算

V = 0.234(十进制) = 0.00111011111001110110110010(二进制) = 1.11011111001110110110010 * 2^-3 ,则 s=0 ,M= 1.110111110,E=-3。

根据 IEEE754 标准转化 :

以 单精度为例

s 不变
E=-3+127=124(十进制) = 0111_1100(二进制)

M=110_1111_1001_1101_1011_0010

结果将他们连接 0_0111_1100_110_1111_1001_1101_1011_0010(s_E_M)

可以在线校验结果

小结

这篇文章详细介绍了计算机如何存储整数以及浮点数,个人也是从朦胧状态到理解透彻,参考了众多大佬的文章,在此表示感谢。有问题的朋友可以通过邮箱联系到我 jake.zou.me@gmail.com

计算机之美妙不可言啊!

参考文档

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

推荐阅读更多精彩内容