起因

在代码 review 的过程中,总是发现有人在数据类型转换(reinterpret_cast)、大小端的问题(什么情况下需要考虑大小端,什么情况下不需要考虑)上犯错误,究其原因是没有彻彻底底地搞懂数据的二进制表示。我想写篇文章,用通俗易懂的语言把这件事情说明白,通俗易懂到我希望我的小白女友也能看懂。于是我就尝试着先做些铺垫,给她讲了些基础。发现效果出奇的好,于是赶紧把这一过程记录如下。

0 和 1 的世界

计算机的世界只有 0 和 1,所有的数据都由 0 和 1 的组合:数字、字母、汉字、图片、音乐、电影、游戏、网页等都可以由很多的 0 和 1 组成。

计算机如何知道一长串的 0 和 1 是什么含义呢?

比如 0100 0001 可能表示数字 65,可能表示大写字母A,可能和更多的 0 和 1 共同组成一个汉字,也可能表示图片上某个点的颜色,其意义完全取决于人们约定的规则

比特和字节

正着说:每一个 0 和 1 叫做一个比特(bit),8 个比特组成一个字节(Byte)。字节是计算机的基本单位,通常计算机一次最少处理一个字节。

例如:人们常说的一个 Word 文档 100 KB,一张图片 2 MB,一首歌 10 MB,一部电影 4 GB,内存 8 GB,硬盘 512 GB 等等。这里的大“B”就是 Bytes,字节。

比特(bit)最常见于宽带的宣传:例如 500M 宽带的完整单位是 500 Mbps(注意这里是小“b”,不是大“B”)。bps 即 bits per second,500Mbps 指的是每秒最大传输 500 兆比特(bit)。所以 500M 的宽带最快下载速度不是 500 MB/s,而是 500/8 = 62.5 MB/s。

反着再说一次:一个字节(byte)有 8 个比特(bit);每个比特只能是 0 或 1,8 个比特一共有 2^8 = 256 种组合,可以代表 256 种含义(具体含义完全取决于人们约定的规则)。

数字的二进制表示:用 0 和 1 表示数字

首先想到用 8 个比特表示 0-255:人们约定,高位到低位每个 bit 有不同的权重,代表不同的值,如此便可用 8 个 bit 表示 0-255 的所有数字。

高位 -> 低位 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
权重 128 64 32 16 8 4 2 1
举例:0 0 0 0 0 0 0 0 0
举例:35 0 0 1 0 0 0 1 1
举例:65 0 1 0 0 0 0 0 1
举例:128 1 0 0 0 0 0 0 0
举例:255 1 1 1 1 1 1 1 1

对于这种不考虑负数的情况,我们称之为无符号数

那如何表示一个负数(有符号数)?

有很多种方法,只要约定好一个规则即可。比如我们可以约定,最高位 bit7 代表符号位,0 代表正数,1 代表负数。于是一个字节,8 个 bit 可以表示 -127 ~ 127 的数字。注意其中 0 有两种表示,+0 和 -0。

高位 -> 低位 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
权重 +/- 64 32 16 8 4 2 1
举例:+0 0 0 0 0 0 0 0 0
举例:-0 1 0 0 0 0 0 0 0
举例:35 0 0 1 0 0 0 1 1
举例:-65 1 1 0 0 0 0 0 1
举例:127 0 1 1 1 1 1 1 1
举例:-127 1 1 1 1 1 1 1 1

现实计算机世界的负数几乎都是补码表示。和无符号数的规则的差别仅在最高位的权重为。于是一个字节,8 个 bit 可以表示 -128 ~ 127 的数字。注意其中 0 只有一种表示。

高位 -> 低位 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
权重 -128 64 32 16 8 4 2 1
举例:0 0 0 0 0 0 0 0 0
举例:35 0 0 1 0 0 0 1 1
举例:65 0 1 0 0 0 0 0 1
举例:-128 1 0 0 0 0 0 0 0
举例:127 0 1 1 1 1 1 1 1
举例:-127 1 0 0 0 0 0 0 1
举例:-1 1 1 1 1 1 1 1 1

先停一下

看到这里,如果问你,1 0 0 0 0 0 0 0 代表一个什么数字,你要怎么回答?千万别急着回答,回答之前应该先问清楚,要按照什么规则去解析。比如这串 0/1 表示的是一个无符号数还是一个补码表示的有符号数。

如何表示更大的数?比如 10000

用多个字节表示。一个字节不够就两个,两个不够就三个、四个甚至八个十六个,直到够用!用 2 个字节就能够表示 0 - 65535 之间的无符号数,用 4 个字节就能表示 0 - 4294967295 的无符号数!

高位 -> 低位 bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
权重 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1
举例:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
举例:65 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1
举例:255 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
举例:10000 0 0 1 0 0 1 1 1 0 0 0 1 0 0 0 0
举例:40256 1 0 0 1 1 1 0 1 0 1 0 0 0 0 0 0
举例:60666 1 1 1 0 1 1 0 0 1 1 1 1 1 0 1 0

有符号数(补码)也是类似的,只不过最高位的权重为负。用 2 个字节就能够表示 -32768 - -32767 之间的有符号数,用 4 个字节就能表示 -2147483648 - 2147483647 的有符号数!

直接使用上面的表格(二进制表示的 bit 15 到 bit 0 和上面一模一样),但是现在按照补码的规则进行解析(最高位权重为负),于是得到的结果就不一样了。

高位 -> 低位 bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
权重 -32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1
举例:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
举例:65 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1
举例:255 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
举例:-25280 1 0 0 1 1 1 0 1 0 1 0 0 0 0 0 0
举例:-4870 1 1 1 0 1 1 0 0 1 1 1 1 1 0 1 0

十六进制:二进制的简化表示法

二进制要用 8 个 0/1 表示一个 byte,太不方便,为简化表示,十六进制用分别用一个 0-F 表示一个字节的前 4 位和后 4 位。一般还会加上前缀0x,以提醒读者后面是 16 进制表示法。

如何表示带小数点的数(浮点数)?

还是一样,只要约定好一个规则就行。计算机界流行的浮点数规则是 IEEE 定义单精度浮点(4 字节表示)和双精度浮点(8 字节表示)。具体规则比较复杂,不在这里展开。

如何表示字符?

我们可以约定,0000 0001 代表 a,0000 0002 代表 b,以此类推。从 0000 0000 到 1111 1111 的 256 种组合中表示 a-z、A-Z,加上各种标点符号也是绰绰有余。现实计算机世界几乎都心照不宣地采用 ASCII 规则来表示常见的英文字符、标点以及一些不显示的控制字符等。ASCII 只用了 7 个 bit。

进制 十进制 十六进制 字符/缩写 解释
00000000 0 00 NUL (NULL) 空字符
00001010 10 0A LF/NL(Line Feed/New Line) 换行键
00001101 13 0D CR (Carriage Return) 回车键
00100000 32 20 (Space) 空格
00100001 33 21 !
00101100 44 2C ,
00101110 46 2E .
00110000 48 30 0
00110001 49 31 1
00110010 50 32 2
01000000 64 40 @
01000001 65 41 A
01000010 66 42 B
01000011 67 43 C
01011000 88 58 X
01011001 89 59 Y
01011010 90 5A Z
01100001 97 61 a
01100010 98 62 b
01100011 99 63 c
01111000 120 78 x
01111001 121 79 y
01111010 122 7A z
01111111 127 7F DEL (Delete) 删除

如何表示汉字?

一个字节一共就 256 种排列组合,就算每个组合代表一个汉字,也只能表示 256 个汉字,这显然是不够的。要想表示一个汉字,至少需要 2 个字节。这样就有 2^16 = 65536 种排列组合,可以表示 65536 个汉字了。应对常见的汉字已经不成问题。GB2312 编码就是用两个字节给汉字编码的。具体编码规则网上找得到,这里不详细展开。

如何表示韩文、日文、阿拉伯文等所有字符?

每个国家、地区都有自己的编码方式。比如同样的一串数字 1011 0000 1010 0001 在GB2312 编码下代表汉字“啊”,而在某种日文编码规则中则可能代表一个日文字符。

比如一个日本程序员开发了一个软件,在日文编码的机器上可以正常显示日文,但是如果拿到中文编码的机器上就会显示乱码。为解决这一问题,推出了 Unicode 编码。Unicode 采用 4 字节编码,可以表示 2^32 = 4294967295 个字符,足够容纳目前世界上所有已知的字符了,甚至包括各种 emoji 表情!

总结

计算机的世界由 0/1 组成,数字、字母、图片等等所有信息都由一串串的 0/1 表示。8 个比特组成一个字节,字节是计算机的基本单位。一个字节可以表示 2^8 = 256 种含义,如何解析完全取决于人们约定的规则。如果一个字节不足以表示所有的范围、可能性,就用多个字节表示。

从零开始给女朋友讲计算机 1 - 从比特、字节、补码到 ASCII、GB2312、Unicode的更多相关文章

  1. Java 第十一届 蓝桥杯 省模拟赛 计算机存储中有多少字节

    计算机存储中有多少字节 题目 问题描述 在计算机存储中,12.5MB是多少字节? 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余 ...

  2. 我给女朋友讲编程html系列(4) -- html常用简单标签

    今天似乎有点感冒,浑身无力,在操场上躺了半个小时,好了许多.好了,废话不说了,还是写今天的教程吧. 1,html中的换行标签是 br,写法是: <br /> 2,html中有一些特殊的字符 ...

  3. 萌新web前端从零开始(1)——计算机入门

    前言:这是一个萌新从零开始的学习之路,与大家分享自己的看法与见解,还请指出错误与遗漏点方便改正. 1.认识计算机. 计算机语言常见的有C,PHP,Ruby,Java,C#,Basic,JS,C++等, ...

  4. 我给女朋友讲编程总结建议篇,怎么学习html和css

    总共写了11篇博客了,7篇讲html的,4篇讲网络的.不敢说写的多么好吧,最起码的是我迈出了写作的第一步,写作的过程中了解了一些其他的知识,比如SEO.几种重定向等,由于个人能力和见识有限,写出来的东 ...

  5. 我给女朋友讲编程html系列(3) --html中的超链接标签-a标签 和 框架frame与框架集frameset

    我们浏览网页的时候,当单击某段文字或图片时,就会打开一个新的网页,这里面就使用了超链接. 就比如下图是一个导航类网页,当你单击某个链接就会打开新的网页. 比如,我拿我的qq空间“金河访谈”举例,新建一 ...

  6. 我给女朋友讲编程html系列(2) --Html标题标签h1

    Html是一门标签语言,因此学习Html最快的方式就是学习使用html标签. html标题标签:h1,h2,h3,h4,h5,h6 标题标签总共有6个,h1,h2,h3,h4,h5,h6,从h1到h6 ...

  7. 我给女朋友讲编程html系列(1) -- Html快速入门

    Html是一门编写网页的语言. 在我学习Html的时候,网上的很多朋友说,直接使用[记事本]编写就行了,最后保存为后缀名为 .html 的文件. 不过,我建议你用[Nodepad++]来编写网页,用这 ...

  8. 北京大学Cousera学习笔记--3-计算导论与C语言基础-第一讲.计算机的基本原理-计算机怎么计算-数的二进制

    思考问题 1.“数”在计算机中是如何表示的? 2.逻辑上“数”是怎么运算的? 3.物理上“数”的计算是怎么实现的? 从图灵机计算问题得出: 1.字母表中的符号越多(几进制),读入移动次数减少,但程序数 ...

  9. 我给女朋友讲编程CSS系列(3) CSS如何设置字体的类型、大小、颜色,如何使用火狐浏览器的Firebug插件查看网页的字体

    一.CSS如何设置字体的类型.大小.颜色 设计网页时,一般设置body的字体,让其他标签继承body的字体,这样设置特别方便,但是标题标签h1到h6和表单标签(input类型)是没有继承body的字体 ...

随机推荐

  1. [转]CAP和BASE理论

    1. CAP理论 2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想.2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证 ...

  2. Lua学习高级篇

    Lua学习高级篇 之前已经说了很多,我目前的观点还是那样,在嵌入式脚本中,Lua是最优秀.最高效的,如果您有不同的观点,欢迎指正并讨论,切勿吐槽.这个系列完全来自于<Programming in ...

  3. node.js学习(4)事件

    1 导入事件库

  4. Python3.6 的字典为什么会快

    作者:青南链接:https://zhuanlan.zhihu.com/p/73426505来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 在Python 3.5(含)以 ...

  5. 摄像头 ISP 调试的入门之谈(经验总结)

    在讲述本文之前,我尽量以一个什么也不清楚的初学到入门的用词来阐述什么是 ISP 调试,以及为什么需要调试. 如果你从来都没有接触过什么是摄像头 ISP 调试,我想这个文章可以给你一些启发和关键词. 因 ...

  6. 视频动作定位的分层自关注网络:ICCV2019论文解析

    视频动作定位的分层自关注网络:ICCV2019论文解析 Hierarchical Self-Attention Network for Action Localization in Videos 论文 ...

  7. Java 面试题关于包装类

    这几个问题的知识点涉及的内容非常的刁钻,值得自己好好的理解. 问以下程序的输出结果是: 问题一: Object object=true ? new Integer(1):new Double(2.0) ...

  8. antd组件库BackTop组件设置动态背景图片的问题

    有这么一个需求,利用antd组件库中的BackTop组件的逻辑,但是自己写样式. 我的目标样式是:有两张图片,一张是normal(正常情况),一张是hover(悬停情况). 这时候就要用到css的动画 ...

  9. Kubernetes 实战——配置应用(ConfigMap、Secret)

    配置容器化应用的方式:①命令行参数:②环境变量:③文件化配置 一.向容器传递命令行参数或环境变量 这两种方式在 Pod 创建后不可被修改 1. 在Docker中定义命令与参数 ENTRYPOINT:容 ...

  10. python operator操作符函数

    本模块主要包括一些Python内部操作符对应的函数.这些函数主要分为几类:对象比较.逻辑比较.算术运算和序列操作.