位与位置记数法
位只有 0、1 两种值。八位字节有 256 种模式,含义取决于约定:同一个字节可以是整数、字符的一部分或指令。二进制从右向左的权重为 1、2、4、8 等,因此 1101 表示 8+4+1=13。十六进制权重为 1、16、256,A–F 表示 10–15,每个十六进制数字对应四位。0x2D 即 0010 1101,十进制为 45。
对非负整数反复除以 2,记录余数,再倒序读取即可编码。前导零改变位宽,不改变无符号值。
二进制 10110 的十进制值是多少?
完整解答
16+4+2=22。
位宽、范围与溢出
w 位有 2^w 种模式。无符号整数范围为 0 到 2^w−1,八位可表示 0–255。255 加一需要九位。固定宽度系统必须规定越界行为:拒绝、回绕、饱和或报错。这些行为不同,不能随意替换。本例转换器拒绝超范围值。
Python 整数按需增长,受内存限制,因此 255+1 本身不会在八位回绕。若要明确模拟八位回绕,可用 (value+1)%256。即使语言整数不固定宽度,存储格式与硬件接口仍可能限制位宽。
六位能表示多少个无符号值?
完整解答
64 个:0 到 63。
有符号整数与字节序
补码把最高位权重设为 −2^(w−1),其余位保持正权重。八位 11111111 为 −128+127=−1;10000000 为 −128,范围是 −128 到 127,而非 −127 到 127。把位模式先看成无符号 u,最高位为 1 时减去 256。
多字节值还需要字节序。0x1234 的大端存储先放 12 再放 34,小端则先 34 再 12。字节序与有无符号是不同选择,交换数据时要同时规定宽度、符号性和字节序。
八位 11111110 的无符号与有符号解释分别是什么?
完整解答
无符号为 254;补码为 254−256=−2。
文字编码后才成为字节序列
Unicode 为文字符号分配码位,UTF-8 把码位编码为字节。ASCII 字符占一个 UTF-8 字节,其他许多码位占多个。A中 有两个码位,但占四个 UTF-8 字节。Python str 表示文字,bytes 表示字节序列;进入字节接口时编码,读取时按约定编码解码。
一个可见字符可能由多个码位组成,例如字母加组合重音,因此码位数量不一定等于可见字符数量。任意截断字节可能切开多字节序列,导致解码失败。编码约定是数据格式的一部分。
为什么文字长度可能不同于 UTF-8 编码后的字节长度?
完整解答
前者统计码位,后者统计字节,一个码位可能占多个字节。
浮点数与精确替代方案
浮点格式保存符号、缩放后的有效数字和指数。有限二进制小数的分母是二的幂。0.1=1/10 的二进制展开重复,有限存储只能舍入。常见 Python 平台上 0.1+0.2 略不同于 0.3,这是表示问题,并非加法任意失效。
近似测量需要按问题选择容差,尤其在零附近结合相对与绝对容差。精确金额可用最小货币单位的整数,或从字符串构造 Decimal。Decimal 也有有限精度上下文,并非每次除法都精确;Fractions 可精确保留有理数,但分子分母可能增长。
0.5 与 0.1 能用有限二进制位精确表示吗?
完整解答
0.5=1/2 可以;0.1=1/10 不可以。
常见误解
- 位模式需要解释规则;未规定有符号格式时,开头的 1 不代表负数。
- 字节长度、码位数量与可见字符数量不同。
- 改变显示精度不会改变保存的浮点值。
实验准备
下载脚本,在终端中使用 Python 3.11 或更新版本运行:python m02_binary.py. Windows 也可使用 py -3;部分系统使用 python3。实验仅用标准库。先预测结果,再运行并完成变体。不要使用 -O,以保留断言。下方输出由构建器实际运行捕获,两种语言使用相同代码与输出。
实验 1 — 构建转换器
跟踪循环中的余数。脚本检查往返转换,并明确拒绝溢出。
"""Base conversion and fixed-width signed interpretation."""
def binary(value, width=8):
if not 0 <= value < 2 ** width:
raise ValueError("value does not fit unsigned width")
bits = []
for _ in range(width):
bits.append(str(value % 2))
value //= 2
return "".join(reversed(bits))
for value in [0, 13, 127, 128, 255]:
bits = binary(value)
signed = value if value < 128 else value - 256
assert int(bits, 2) == value
print(f"{bits}: unsigned={value:3}, signed={signed:4}, hex={value:02x}")
try:
binary(256)
except ValueError:
print("256 rejected at width 8")
else:
raise AssertionError("overflow must be rejected")
00000000: unsigned= 0, signed= 0, hex=00
00001101: unsigned= 13, signed= 13, hex=0d
01111111: unsigned=127, signed= 127, hex=7f
10000000: unsigned=128, signed=-128, hex=80
11111111: unsigned=255, signed= -1, hex=ff
256 rejected at width 8
- 预测 42 的输出。
- 使用四位,测试 15 和 16。
- 解释为何有符号解释在 128 改变。
完整解答
42 是 00101010,十六进制为 2a。四位的 15 是 1111,16 被拒绝。八位符号位权重为 −128,因此无符号 u 至少为 128 时,有符号解释为 u−256。
实验 2 — 检查字节与舍入
运行文字往返转换,比较 float 与 Decimal。截断案例明确验证解码错误。
"""Separate text from bytes, and approximate numbers from decimal arithmetic."""
from decimal import Decimal
from math import isclose
text = "A中"
encoded = text.encode("utf-8")
print("code points:", len(text), "bytes:", len(encoded), "hex:", encoded.hex())
assert encoded.decode("utf-8") == text
print("float:", 0.1 + 0.2, "exact equality:", 0.1 + 0.2 == 0.3)
assert isclose(0.1 + 0.2, 0.3, rel_tol=1e-12, abs_tol=1e-12)
exact = Decimal("0.1") + Decimal("0.2")
print("decimal:", exact)
assert exact == Decimal("0.3")
try:
encoded[:2].decode("utf-8")
except UnicodeDecodeError:
print("truncated UTF-8 rejected")
code points: 2 bytes: 4 hex: 41e4b8ad
float: 0.30000000000000004 exact equality: False
decimal: 0.3
truncated UTF-8 rejected
- 把文字改为 ASCII 的 ABC。
- 尝试把原文字编码为 ASCII。
- 比较 Decimal('0.1') 与 Decimal(0.1),解释差异。
完整解答
ABC 有三码位、三个 UTF-8 字节。ASCII 无法编码 中,会抛出 UnicodeEncodeError。从 float 构造 Decimal 保留已舍入的浮点值,从字符串 '0.1' 构造则保留预期十进制输入。
练习与完整解答
先尝试,再展开解答。★ 应用概念;★★ 结合概念;★★★ 进行设计或证明。
把 45 写成八位二进制与十六进制。
完整解答
45=32+8+4+1,所以为 00101101。按四位分组 0010、1101,得到 0x2D。
十位无符号范围是多少?
完整解答
2^10=1024 种模式,范围为 0–1023。
用八位补码编码 −5。
完整解答
256−5=251,二进制为 11111011;按 −128+123 解码得到 −5。
用两种字节序存储两字节的 0x0102。
完整解答
大端为 01 02,小端为 02 01,按各自约定都表示 258。
为什么 A中 的前两个字节无法解码?
完整解答
A 占一字节,中 占三字节;截取部分只保留 A 与 中 的第一个字节,UTF-8 序列不完整。
1/8 与 1/10 哪个二进制展开有限?
完整解答
1/8 的二进制为 0.001,8 是二的幂;约分后分母 10 含因子五,所以 1/10 重复。
设计精确到两位小数且不含分币的价格存储。
完整解答
保存整数分:12.34 保存为 1234,精确求和,在显示边界格式化。还需规定货币、最大金额与税费等运算的舍入规则。
记录包含最大 1000 的无符号计数。规定宽度、符号性与字节序。
完整解答
数学上十位足够。实际字节对齐字段可用两字节无符号大端,仅接受 0–1000。应拒绝保留或超范围值,不能假定每个十六位模式都有效。
自测
选择答案查看反馈,重置后可重做。无需 JavaScript 也可阅读答案表。
一个字节有多少种模式?
八位补码 10000000 是什么?
Python int 在 255 后自动回绕吗?
UTF-8 编码什么?
哪个分数可用有限二进制精确表示?
哪种构造保留十进制输入 0.1?
答案表
- A — 八位产生 2^8 种模式。
- B — 最高位权重为 −128。
- C — Python 整数不限制为八位。
- A — 文字与字节表示不同。
- B — 四是二的幂。
- C — 字符串避免中间的二进制近似。
引导阅读
- Python 浮点教程 — 阅读表示误差说明,解释何时适合用容差。
- Python Unicode 指南 — 阅读编码与解码错误,区分 str 和 bytes。
复习与下一步
编码 29,解释一个八位负数,并说明 A中 为什么需要四个 UTF-8 字节。为目录中的数量与价格选择表示。模块 03 将使用这些值构建程序。
关键术语
| 术语 | 含义 |
|---|---|
| 位 | 具有两个可能值的二进制数字。 |
| 补码 | 最高位权重为负的有符号解释。 |
| 编码 | 值到表示形式的约定映射。 |
| 舍入 | 选择接近预期值的可表示值。 |