10.计算机二进制和字节设计
目录介绍
- 01.进制数据的由来
- 1.1 先来看一个场景
- 1.2 什么是进制
- 1.3 二进制的由来
- 1.4 数据传输用什么
- 02.为何选择二进制
- 2.1 计算机为何不选十进制
- 2.2 理解逢二进一
- 2.3 十进制转二进制
- 03.位和字节设计思想
- 3.1 位的设计
- 3.2 字节的设计
- 3.3 字符的设计
- 04.字符串的表示
- 4.1 从编码到数字
- 4.2 二进制存储空间
- 4.3 字符集的由来
- 4.4 乱码为何出现
01.进制数据的由来
1.1 先来看一个场景
- 遇到的乱码究竟是怎么回事儿。我们平时在开发的时候,所说的 Unicode 和 UTF-8 之间有什么关系。
1.2 什么是进制
- 进制:就是进位制,是人们规定的一种进位方法。对于任何一种进制--X进制,就表示某一位置上的数运算时是逢X进一位
- 二进制就是逢二进一,二进制: 由0,1组成。
- 八进制是逢八进一,八进制: 由0,1,…7组成。以数字“0”开头
- 十进制是逢十进一,十进制: 由0,1,…9组成。整数默认是十进制的
- 十六进制是逢十六进一,十六进制: 由0,1,…9,a,b,c,d,e,f(大小写均可)。以0x开头
1.3 二进制的由来
- 二进制的由来
- 二进制,并认为这是世界上数学进制中最先进的,20世纪被称作第三次科技革命的重要标志之一的计算机的发明与应用,其运算模式正是二进制。
- 八进制的由来
- 二进制早期由电信号开关演变而来。一个整数在内存中一样也是二进制的,但是使用一大串的1或者0组成的数值进行使用很麻烦。
- 所以就想把一大串缩短点,将二进制中的三位用一位表示。这三位可以取到的最大值就是7,超过7就进位了,这就是八进制。
- 十六进制的由来
- 对于过长的二进制变成八进制还是较长,所以出现的用4个二进制位表示一位的情况,四个二进制位最大是15,这就是十六进制。
1.4 二进制数据传输
- 计算机中使用二进制传输数据是因为计算机内部的处理和存储都是以二进制形式进行的。
- 二进制是一种只包含0和1两个数字的数制系统,与计算机内部的电子元件工作原理相匹配。
- 以下是一些原因说明为什么计算机使用二进制传输数据:
- 简单和可靠:二进制只有两个状态,0和1,使得数据传输和处理更加简单和可靠。计算机内部的电子元件可以轻松地识别和处理这两个状态。
- 容易实现:计算机内部的逻辑门电路(如与门、或门、非门等)可以直接使用二进制信号进行操作。这样,计算机的设计和实现变得更加简单和高效。
- 兼容性:二进制是一种通用的数制系统,可以表示和处理各种类型的数据,包括数字、文本、图像、音频等。通过使用二进制传输数据,计算机可以处理和存储各种不同类型的信息。
- 可扩展性:二进制的位表示形式可以轻松地扩展到更高的位数,以适应更大范围的数值和数据。这使得计算机可以处理和存储非常大的数据量。
- 效率和速度:二进制的传输和处理速度通常比其他数制系统更快。这是因为计算机内部的电子元件可以更快地切换和处理二进制信号。
02.为何选择二进制
2.1 计算机为何不选十进制
- 电报机只有一个按钮,按下就是输入信号,按的时间短一点,就是发出了一个“点”信号;按的时间长一些,就是一个“划”信号。只要一个手指,就能快速发送电报。
- 制造一台电报机也非常容易。电报机本质上就是一个“蜂鸣器 + 长长的电线 + 按钮开关”。
- 蜂鸣器装在接收方手里,开关留在发送方手里。双方用长长的电线连在一起。当按钮开关按下的时候,电线的电路接通了,蜂鸣器就会响。
- 短促地按下,就是一个短促的点信号;按的时间稍微长一些,就是一个稍长的划信号。
2.2 理解逢二进一
- 二进制和我们平时用的十进制,其实并没有什么本质区别,只是平时我们是“逢十进一”,这里变成了“逢二进一”而已。
- 每一位,相比于十进制下的 0~9 这十个数字,我们只能用 0 和 1 这两个数字。
- 任何一个十进制的整数,都能通过二进制表示出来。
- 把一个二进制数,对应到十进制,非常简单,就是把从右到左的第 N 位,乘上一个 2 的 N 次方,然后加起来,就变成了一个十进制数。
- 当然,既然二进制是一个面向程序员的“语言”,这个从右到左的位置,自然是从 0 开始的。
- 比如 0011 这个二进制数,对应的十进制表示
- 就是 0\times2^{3} \dotplus 0\times2^{2} \dotplus 1\times 2^{1}\dotplus 1\times 2^{0}= 3,代表十进制的 3。
2.3 十进制转二进制
- 把一个十进制的数,转化成二进制,使用短除法就可以。
- 也就是,把十进制数除以 2 的余数,作为最右边的一位。然后用商继续除以 2,把对应的余数紧靠着刚才余数的右侧,这样递归迭代,直到商为 0 就可以了。
- 比如,我们想把 13 这个十进制数,用短除法转化成二进制,需要经历以下几个步骤:
- 因此,对应的二进制数,就是 1101。
商 余数 二进制位 13/2 6 1 1 6/2 3 0 0 3/2 1 1 1 1/2 0 1 1
03.位和字节设计思想
3.1 位的设计
- 位:"位(bit)"是电子计算机中最小的数据单位。
- 二进制数据中,每一位的状态只能是0或1。
- 八进制数据中,每一位的状态只能是0,1,…9。
- 十六进制数据中,每一位的状态只能是0,1,…9,a,b,c,d,e,f(大小写均可)。
- 不同进制的转化
- 每1个八进制位对应2个二进制位。01 ——> 0001
- 每1个十六进制位对应4个二进制位。OX01 ——> 0000 0001
3.2 字节的设计
- 字节:8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。
- 一个字节(byte)由8个二进制位(bits)组成。
- 每个二进制位可以表示0或1两个状态。
- 因此,一个字节可以表示256(2的8次方)个不同的值,范围从0到255。
- 一个字节(byte)可以表示为2个十六进制位。
- 每个十六进制位可以是0-9的数字(0-9)或A-F的字母(10-15),共16个可能的值。
- 因此,一个字节的十六进制表示范围是00到FF。其中,00表示0,FF表示255。
3.3 字符的设计
- 一个字符(char)由16个二进制位(bits)组成。
- 每个二进制位可以表示0或1两个状态。
- 一个字符的二进制表示范围是0000000000000000到1111111111111111。其中,0000000000000000表示0,1111111111111111表示65535。
- 一个字符(char)占用2个字节(16位)。
- Java使用Unicode字符编码,其中每个字符都用16位表示。Java的char类型是无符号的,范围从0到65535(0x0000到0xFFFF)。
- 一个字符(char)可以表示为4个十六进制位。
- 每个十六进制位可以是0-9的数字(0-9)或A-F的字母(10-15),共16个可能的值。
- 一个字符的十六进制表示范围是0000到FFFF。其中,0000表示0,FFFF表示65535。
- 设计字符(char)的主要原因是为了表示和处理文本数据。以下是一些原因:
- 文本表示&处理:字符用于表示文本中的字母、数字、符号和其他可打印字符。通过字符,可以方便地处理和操作文本数据。
- 字符编码:字符在计算机系统中使用字符编码来表示。常见的字符编码方案如ASCII、Unicode等,它们将字符映射到数字值,使得计算机能够处理和存储文本数据。
- 字符串操作:字符是构建字符串(String)的基本单位。字符串是一系列字符的集合,用于表示和处理更复杂的文本数据。
04.字符串的表示
4.1 从编码到数字
- 不仅数值可以用二进制表示,字符乃至更多的信息都能用二进制表示。
- 最典型的例子就是字符串(Character String)。最早计算机只需要使用英文字符,加上数字和一些特殊符号,然后用 8 位的二进制,就能表示我们日常需要的所有字符了,这个就是我们常常说的 ASCII 码。
- ASCII 码就好比一个字典,用 8 位二进制中的 128 个不同的数,映射到 128 个不同的字符里。
- 比如,小写字母 a 在 ASCII 里面,就是第 97 个,也就是二进制的 0110 0001,对应的十六进制表示就是 61。
- 而大写字母 A,就是第 65 个,也就是二进制的 0100 0001,对应的十六进制表示就是 41。
- 在 ASCII 码里面,数字 9 不再像整数表示法里一样,用 0000 1001 来表示,而是用 0011 1001 来表示。
- 字符串 15 也不是用 0000 1111 这 8 位来表示,而是变成两个字符 1 和 5 连续放在一起,也就是 0011 0001 和 0011 0101,需要用两个 8 位来表示。
4.2 二进制存储空间
- 最大的 32 位整数,就是 2147483647。
- 如果用整数表示法,只需要 32 位就能表示了。但是如果用字符串来表示,一共有 10 个字符,每个字符用 8 位的话,需要整整 80 位。比起整数表示法,要多占很多空间。
- 这也是为什么,很多时候我们在存储数据的时候,要采用二进制序列化这样的方式,而不是简单地把数据通过JSON,这样的文本格式存储来进行序列化。
- 不管是整数也好,浮点数也好,采用二进制序列化会比存储文本省下不少空间。
4.3 字符集的由来
- 字符集的由来主要是为了增加计算机字符的表示
- ASCII 码只表示了 128 个字符,一开始倒也堪用,毕竟计算机是在美国发明的。
- 然而随着越来越多的不同国家的人都用上了计算机,想要表示譬如中文这样的文字,128 个字符显然是不太够用的。
- 于是,计算机工程师们开始各显神通,给自己国家的语言创建了对应的字符集(Charset)和字符编码(Character Encoding)。
- 字符集,表示的可以是字符的一个集合。
- 比如“中文”就是一个字符集,不过这样描述一个字符集并不准确。
- 想要更精确一点,我们可以说,“第一版《新华字典》里面出现的所有汉字”,这是一个字符集。
- 这样,我们才能明确知道,一个字符在不在这个集合里面。比如,我们日常说的 Unicode,其实就是一个字符集,包含了 150 种语言的 14 万个不同的字符。
- 字符编码则是对于字符集里的这些字符,怎么一一用二进制表示出来的一个字典。
- Unicode,就可以用 UTF-8、UTF-16,乃至 UTF-32 来进行编码,存储成二进制。
- 所以,有了 Unicode,其实我们可以用不止 UTF-8 一种编码形式,我们也可以自己发明一套 GT-32 编码,比如就叫作 Geek Time 32 好了。只要别人知道这套编码规则,就可以正常传输、显示这段代码。
4.4 乱码为何出现
- 同样的文本,采用不同的编码存储下来。如果另外一个程序,用一种不同的编码方式来进行解码和展示,就会出现乱码。
- 这就好像两个军队用密语通信,如果用错了密码本,那看到的消息就会不知所云。
- 来看一个乱码的案例
- 在中文世界里,最典型的就是“手持两把锟斤拷,口中疾呼烫烫烫”的典故。
- 既然今天要彻底搞清楚编码知识,我们就来弄清楚“锟斤拷”和“烫烫烫”的来龙去脉。
- 首先,“锟斤拷”的来源是这样的。
- 如果我们想要用 Unicode 编码记录一些文本,特别是一些遗留的老字符集内的文本,但是这些字符在 Unicode 中可能并不存在。
- 于是,Unicode 会统一把这些字符记录为 U+FFFD 这个编码。如果用 UTF-8 的格式存储下来,就是\xef\xbf\xbd。
- 如果连续两个这样的字符放在一起,\xef\xbf\xbd\xef\xbf\xbd,这个时候,如果程序把这个字符,用 GB2312 的方式进行 decode,就会变成“锟斤拷”。
- 这就好比我们用 GB2312 这本密码本,去解密别人用 UTF-8 加密的信息,自然没办法读出有用的信息。
- 而“烫烫烫”,则是因为如果你用了 Visual Studio 的调试器,默认使用 MBCS 字符集。
- “烫”在里面是由 0xCCCC 来表示的,而 0xCC 又恰好是未初始化的内存的赋值。于是,在读到没有赋值的内存地址或者变量的时候,电脑就开始大叫“烫烫烫”了。
03.原码/反码/补码
3.1 原码/反码/补码设计
- 原码/反码/补码设计思想
- 人脑可以知道第一位是符号位,可以根据符号位对真值得绝对值进行加减乘除。但是对于计算机来说,加减乘除是最基本的运算,要设计得尽量简单。
- 计算机辨别符号位会让计算机的设计电路变得很复杂,于是人们想出了让符号位也参与到运算上来。
- 减去一个数,等于加上他的负数,0就代表正数,1代表负数 第1位为符号位。
3.2 正数表示法
- 假设机器字长(处理的位数为8位),数字:1 原码,反码,补码
- 原码:00000001
- 反码:00000001
- 补码:00000001
3.3 负数表示法
- 数字:-1 原码,反码,补码
- 原码:符号位为1其余各位不变 1000 0001
- 反码:符号位不变,其余各位取反 1111 1110
- 补码:符号位不变,其位各位取反后加1(对补码取反加1 得到原码) 1111 1111
05.进制转化实践
5.1 Hex十六进制转化
5.2 Oct八进制转化
5.3 Bin二进制转化设计
07.二进制表达数据
7.1 二进制表示方式
- 二进制是一种计算机中常用的数值表示方式,它使用两个数字0和1来表示数据。
- 在计算机中,所有的数据都可以用二进制来表示,包括整数、浮点数、字符、图像、音频等。
- 二进制位是计算机中最小的存储单位,通常以8个二进制位为一组,称为字节(byte)。
- 以下是一些常见数据类型的二进制表示方式:
- 整数:整数可以使用二进制补码表示。正整数的二进制表示与其在十进制中的表示相同,负整数的二进制表示是其绝对值的二进制补码。
- 浮点数:浮点数使用IEEE 754标准来表示。它将浮点数分为符号位、指数位和尾数位,使用二进制表示。
- 字符:字符可以使用ASCII码或Unicode编码来表示。每个字符都有对应的二进制编码,可以使用二进制位来表示字符。
- 图像和音频:图像和音频数据可以使用二进制位来表示像素值或采样值。每个像素或采样点都可以
7.2 二进制表达整数
- 在二进制中,整数可以使用二进制补码表示。
- 二进制补码是一种表示有符号整数的方法,它使用固定位数的二进制数来表示整数。
- 在二进制补码表示中,最高位(最左边的位)用于表示符号位,0表示正数,1表示负数。其余位表示整数的数值部分。
- 二进制补码表示整数的一般规则:
- 正整数:正整数的二进制表示与其在十进制中的表示相同。例如,十进制数5的二进制表示为0000 0000 0000 0101。
- 负整数:负整数的二进制表示是其绝对值的二进制补码。二进制补码的计算方法是,将正整数的二进制表示按位取反(0变为1,1变为0),然后加1。例如,十进制数-5的二进制表示为1111 1111 1111 1011。
- 需要注意的问题如下所示
- 二进制补码表示中,整数的位数是固定的。例如,使用8位二进制补码表示整数时,可以表示的范围是-128到127(包括0)。如果超出了这个范围,就会发生溢出。
- 例如,使用8位二进制补码表示整数时,十进制数-128的二进制表示为10000000,十进制数127的二进制表示为01111111。
7.3 二进制表达浮点数
- 浮点数在计算机中的存储方式其实是以补码的形式。在计算机中,数字都是用补码来表示与存储的。
- 正数的补码是其二进制表示的,负数的补码是其正数的二进制取反再加一。然后就是为什么0.3+0.6会导致精度丢失了。
- 0.3转化为二进制为0.0100110011001100)结果是保留16位的,然后导致了精度的丢失;
- 0.6转化为0.1001100110011001,结果是保留16位的,也导致了精度的丢失;相加起来就是0.1110011001100101再转化为十进制为0.8999786376953125和上面结果不同,但差不多应该是计算精度的不同吧。
7.4 二进制表达字符
- 在计算机中,字符可以使用不同的编码方案来进行二进制表示。最常见的编码方案是ASCII码和Unicode编码。
- ASCII码:ASCII是一种最早的字符编码方案,使用7位二进制数(0-127)来表示128个字符,包括英文字母、数字、标点符号和一些控制字符。例如,字符'A'的ASCII码表示为二进制数01000001。
- Unicode编码:Unicode是一种更为广泛的字符编码方案。它使用不同的编码方式,如UTF-8、UTF-16等。UTF-8是一种变长编码方式,使用8位二进制数(0-255)来表示字符。UTF-16是一种定长编码方式,使用16位二进制数(0-65535)来表示字符。
- 在二进制中,字符的表示方式取决于所使用的编码方案。
- 例如,字符'A'的ASCII码表示为二进制数01000001,而在UTF-8编码中,字符'A'的表示为01000001。
- 不同的编码方案可能使用不同的位数来表示字符,因此在进行字符编码和解码时,需要使用相应的编码方案来正确地转换二进制数据。
7.5 二进制表达图像
7.6 二进制传输注意点
- 在进行二进制传输时,有一些注意点需要考虑,以确保数据的正确性和可靠性:
- 数据长度:在传输二进制数据时,需要确保发送方和接收方对数据长度的解释是一致的。可以在数据中包含长度信息,或者使用固定长度的数据块进行传输。
- 数据校验:为了确保数据的完整性,可以使用校验和或哈希算法对二进制数据进行校验。发送方在发送数据之前计算校验和,并将其附加到数据中。接收方在接收数据后重新计算校验和,并与接收到的校验和进行比较,以检测数据是否被篡改。
- 压缩和编码:在传输大量二进制数据时,可以考虑使用压缩算法来减少数据的传输量。同时,还可以使用编码方案(如Base64)将二进制数据转换为可打印字符,以便在文本协议中传输。
- 安全性:对于敏感数据的二进制传输,需要考虑数据的加密和身份验证。可以使用加密算法对数据进行加密,以保护数据的机密性。同时,还可以使用数字签名或证书来验证数据的来源和完整性。