Skip to content

Data Encoding

Sign Magnitude

书本中翻译为原码,直译过来是有符号的量,也是最自然的表示方式,符合人的思维。 当用计算机表示数值的时候,我们首先想到将+-符号用单独一个bit来存储:0表示+,1表示-

正数还是其本身的二进制表示方式,负数则使最高位为1,其余部分与其正数部分一致。

示例:对于八位的数值1

  • 1: 0000 0001B
  • -1: 1000 0001B

Ones' Complement

书本中翻译为反码,直译过来的是一个1或者多个1的补集

对于一个正数x,其反码还是本身; 对于负数-x,其用反码表示为 1111·····1 - x

示例:对于八位数的1

  • 1: 0000 0001B
  • -1: 1111 1111B - 0000 0001B = 1111 1110B 可能是因为负数的表示方式刚好是将正数的每一位都进行取反~操作,因此将其翻译为反码。

备注:Complement 表示不可或缺的补充,有互补,更完善和互相依存的意思;同义的Supplement则是额外补充的意思,不是不可或缺的

Two's Complement

翻译为补码,直译过来是2的补集。 对于一个正数x,其补码表示为它的原码。 对于一个负数-x,采用N个bits来存储时,其补码表示为2^N - x 它恰好包含了原码的符号特点,也可以采用加法进行所有的数值计算。之前一直不知道这一块的数学原理,后来发现它利用了数学上模运算取余的机制。 引入补码的目的是为了避免使用反码原码时0的两种表示方式带来的歧义和处理问题。

0的原码:

  • 0000 0000B
  • 1000 0000B

0的反码:

  • 0000 0000B
  • 1111 1111B

还有就是当初设计计算机时只实现了加法器,如果再引入减法器会额外增加更多的电路复杂性。还有就是在计算时要额外处理符号位,这也是增加了电路的复杂性。

因此,人们巧妙的利用了计算机不能表示无穷大的数,只能表示一定范围的数,当表示超出范围的数时会有溢出,也就是超出部分会丢失这个特点,来设计了补码。

TODO: 1000 0000B 代表多少

1
2
3
signed char i = 0B1'0'00'0000;
printf("%i, \n", i);
// -128
  • 可以使用0B前缀直接以二进制形式来定义数据。
  • 使用'来分割二进制序列,它可以出现在0B和第一个数字之后的任意位置。

补码的计算

针对补码求其值,只需要将正负位的权重值的和计算出来即可。 比如,8位的数1000 0001B等于

2^(7) * (-1) + 1 = -127

对于一个w位的整数,正数为x,那么-x的补码为

2^w - x

这可能是补码英文原名的意义所在。

数值范围

  • 对于无符号数,w位的整数范围是0-(2^w-1), 可以表示2^w个数
  • 对于有符号数,w位的整数范围是(-2^(w-1)) - (2^(w-1) - 1), 可以表示2^w个数 负数根据补码的权重计算方式,当正权重的位都是0,负权重位为1时,得到w位数据最小的负数 -2^(w-1)。

所以,8位的有符号数,最小的负数是-2^7 = -128,最大的正数是2^7 - 1 = 127,再加上0,一共是128 + 127 + 1 = 256个数。

TODO

验证溢出的处理,比如-128 + (-128), 127+127

    char a = -128;
    char b = 127;
    char result = (char)(a + a);
    printf("%d, %d\n", result, a+a);

    result = b + b;
    printf("%d, %d\n", result, b + b);

    result = b + 1;
    printf("%d, %d\n", result, b + b);

输出

1
2
3
0, -256
-2, 254
-128, 254

浮点数

浮点数是计算机中表示实数的一种方式,通常用于表示小数。

由三部分组成

  • 符号位:表示数值的正负
  • 指数位
  • 尾数位

相关标准: IEEE 754

Note

浮点数采用单独的编码体系,基于二进制科学计数法。 同样的内存大小,浮点数的数值范围比整型更大,但是精度上会有损失。

字符编码

字符编码是将字符映射到数字的过程,以便计算机可以存储和处理文本。常见的字符编码包括ASCIIUTF-8UTF-16等。

  • ASCII: 使用7位或8位表示字符,支持128或256个字符,主要用于英语字符集。
  • UTF-8: 变长编码,使用1到4个字节表示字符,兼容ASCII,支持全球范围内的字符。
  • UTF-16: 使用2或4个字节表示字符,支持更广泛的字符集,常用于Windows和Java等平台。
  • GB18030/GBK/GB2312: 中文字符编码,GBKGB2312的扩展,支持简体和繁体中文字符。 早期我们国家推出了GB2312标准,支持6763个常用汉字,但是没有包含繁体字和生僻字。 后来又推出了GBK标准,支持2万多个汉字,包含了繁体字和生僻字。 最后推出了GB18030标准,支持超过7万个汉字,包含了所有的常用汉字和生僻字。 字符集数量上GB18030 > GBK > GB2312

字符编码的选择

在选择字符编码时,需要考虑应用的需求和兼容性。 UTF-8是目前最常用的编码方式,适用于大多数场景,尤其是需要支持多语言的应用。 UTF-8主要提升了存储效率,因而在网络传输和存储上更为高效。 定长类编码在字符索引方面更为高效,UTF-8变长编码需要遍历每个字符来获取其长度,可能会影响性能。 GB系列的编码与Unicode编码是两套独立的编码体系,它们只有ASCII码部分是相同的。

全角 & 半角

半角是ASCII码中的字符;全角是中文的字符,属于GB系列编码。 半角字符占用1个字节,全角字符占用2个字节。 从显示效果上看,全角的宽度也比半角要大一倍:半角百分号 % vs 全角百分号 %