第二章 二进制与数据编码 —— 计算机的语言
2.1 为什么计算机使用二进制?
计算机使用二进制的原因:物理上容易实现两种稳定状态(高/低电平、充电/放电、磁化方向),抗干扰能力强,布尔代数直接对应逻辑电路。
二进制转十进制:
二进制 1011.01 = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ + 0×2⁻¹ + 1×2⁻²
= 8 + 0 + 2 + 1 + 0 + 0.25 = 11.25
十进制转二进制(整数部分除2取余):
13 除以 2 得 6 余 1
6 除以 2 得 3 余 0
3 除以 2 得 1 余 1
1 除以 2 得 0 余 1
从下往上读:1101
代码实现进制转换:
# 任意进制转换
def decimal_to_binary(n):
if n == 0:
return "0"
result = ""
while n > 0:
result = str(n % 2) + result
n //= 2
return result
print(decimal_to_binary(13)) # "1101"
print(bin(13)) # "0b1101"
# 十进制转十六进制
print(hex(255)) # "0xff"
print(hex(42)) # "0x2a"
2.2 原码、反码、补码 —— 整数的二进制表示
计算机使用补码表示有符号整数,因为补码可以将减法统一为加法。
以 8 位有符号整数为例:
+5 原码: 0000 0101
-5 原码: 1000 0101
+5 反码: 0000 0101
-5 反码: 1111 1010
+5 补码: 0000 0101
-5 补码: 1111 1011 (反码+1)
验证加法:5 + (-5) = 0
0000 0101
+ 1111 1011
= 1 0000 0000 -> 丢弃进位,得 0000 0000
为什么 -128 能表示(8位补码范围 -128~127):
-128 补码:1000 0000
127 补码:0111 1111
-128 没有对应的原码和反码,是补码特有的。
代码模拟补码加法:
public class ComplementDemo {
public static void main(String[] args) {
// 观察溢出
byte a = 127;
byte b = 1;
byte c = (byte)(a + b); // 溢出
System.out.println(c); // -128
// 手动模拟补码加法
int x = 5, y = -3;
System.out.printf("%d + %d = %d\n", x, y, x + y);
// 查看二进制表示
System.out.println(Integer.toBinaryString(5)); // 101
System.out.println(Integer.toBinaryString(-3)); // 11111111111111111111111111111101
}
}
2.3 浮点数的 IEEE 754 标准
浮点数在计算机中不精确,因为用有限的二进制位数表示无限小数。
单精度(32位)布局:
符号位(1) | 指数位(8) | 尾数位(23)
S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
公式: (-1)^S × 1.M × 2^(E-127)
浮点数精度丢失示例:
# 0.1 的二进制是无限循环 0.0001100110011...
print(0.1 + 0.2) # 0.30000000000000004
# 用 Decimal 避免
from decimal import Decimal, getcontext
getcontext().prec = 50 # 设置精度
print(Decimal('0.1') + Decimal('0.2')) # 0.3
# 浮点数比较的正确方式
def float_equal(a, b, epsilon=1e-10):
return abs(a - b) < epsilon
print(float_equal(0.1 + 0.2, 0.3)) # True
2.4 字符编码 —— 从 ASCII 到 Unicode
ASCII(美国信息交换标准代码): 7位表示 128 个字符(0-127),包括英文字母、数字、标点、控制字符。
# ASCII 码演示
for code in range(65, 91):
print(f"{code}: {chr(code)}") # 65:A, 66:B, ...
中文字符编码演进:
GB2312:中国国家标准,包含 6763 个汉字
GBK:扩展版,包含 21003 个汉字
Big5:繁体中文常用
Unicode:统一所有字符,每个字符有唯一码点(如 U+4E2D 是“中”)
UTF-8:变长编码,兼容 ASCII,是互联网最流行的实现
UTF-8 编码规则:
代码演示乱码与修复:
# 常见乱码原因:编码和解码方式不一致
text = "你好,世界"
# 用 UTF-8 编码
utf8_bytes = text.encode('utf-8')
print(utf8_bytes) # b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c'
# 错误地用 GBK 解码 -> 乱码
garbled = utf8_bytes.decode('gbk', errors='ignore')
print(garbled) # 输出乱码: "浣犲ソ锛屼笘鐣"
# 正确解码
correct = utf8_bytes.decode('utf-8')
print(correct) # 你好,世界