IEEE 754、字符集与 Unicode

共 52 题
📑 题目列表 52 题
#
★★★

1. htonl 与 ntohl 在小端机器上做什么?为何需要?

htonl 与 ntohl 在小端机器上分别做什么?它们存在的意义是什么?

  • 大小端字节序的概念
  • htonl/ntohl 的语义与实现
  • 网络字节序(大端)为何被统一

htonl 把 32 位整数从"主机字节序"转换为"网络字节序"(大端),ntohl 反向转换。在小端机器上,htonl 会把整数的字节顺序反转(例如 0x12345678 存储为字节序 78 56 34 12,网络中看到的是 12 34 56 78);在已是大端的机器上它们是空操作。之所以需要,是因为网络协议(TCP/IP)规定所有多字节字段以网络字节序(大端)传输,不同的 CPU 字节序不同,为了多台机器能正确交换数据,必须在发送端把主机序转成网络序、接收端把网络序转回主机序。

字节序是体系结构的差异,网络层必须提供统一的约定。htonl/ntohl 把"主机字节序"这一平台相关细节封装起来,使代码跨平台可移植,也是"网络字节序"名字的由来。

#include <arpa/inet.h>
#include <stdio.h>
int main() {
    uint32_t x = 0x12345678;
    uint32_t net = htonl(x);   // 小端机器上字节序反转
    uint32_t back = ntohl(net); // 还原为 0x12345678
    printf("%08x %08x %08x\n", x, net, back);
    return 0;
}
#
★★★

2. 把 struct { uint16_t a; uint32_t b; } 的内存布局在小端与大端下分别画出来?

定义 struct { uint16_t a; uint32_t b; },在小端与大端两种字节序下,其内存布局分别是什么?

  • 结构体字段的字节序
  • 字节序实际指的是"多字节数值在内存中的字节存放顺序"
  • 小端与大端下具体内存字节序列的推导

假设 a=0x1122,b=0x33445566,结构体从地址 0 开始(按紧凑布局计算,忽略对齐填充)。小端序(x86)下:地址 0 处存 22 11(a 的低字节在前),地址 2 处存 66 55 44 33(b 的低字节在前),因此内存字节序列为 22 11 66 55 44 33。大端序(老 PowerPC)下:地址 0 处存 11 22,地址 2 处存 33 44 55 66,即内存字节序列为 11 22 33 44 55 66。注意:字节序只影响"多字节数值内部字节的摆放顺序",并不改变字段排列顺序(a 仍在 b 之前)。

字节序是针对单个多字节值的,而不是整个结构体的字段顺序。同一份结构体数据在两个不同字节序的机器上,用各自的方式读取,都能得到正确值;但若把字节序列原样写入文件或网络,另一端用不同字节序解析就会得到错误的值。

#
★★★

3. 给定 16 位 0x00FF 在 BE/LE 下字节序列分别是什么?

给定 16 位数值 0x00FF,在大端(BE)与小端(LE)两种字节序下的字节序列分别是什么?

  • 大端/小端的字节顺序
  • 16 位数值的字节拆分
  • 高低字节的划分与内存地址的对应

0x00FF 的高字节是 0x00,低字节是 0xFF。大端序(高字节在前)下字节序列为 00 FF;小端序(低字节在前)下字节序列为 FF 00。注意 0x00FF 高低字节恰好都在 0x00-0xFF 范围内,很适合演示字节序差异。

大端按照"高位字节放低地址"的顺序,小端相反。内存中看到的字节序列就是按地址递增排列的,因此大端写出的是 00 FF,小端写出的是 FF 00。

#
★★★

4. 给定 32 位整数 0x12345678,分别写出 BE 与 LE 字节序的字节序列?

给定 32 位整数 0x12345678,分别写出大端序(BE)与小端序(LE)的字节序列?

  • 32 位整数按字节拆分
  • 大端/小端排列规则
  • 内存中按地址递增排列的字节序列书写

0x12345678 从高到低四个字节为 12、34、56、78。大端序(高字节在前,存入低地址)为 12 34 56 78;小端序(低字节在前,存入低地址)为 78 56 34 12。

这是字节序最经典的考题。大端与人类书写顺序一致,小端颠倒。x86 采用小端,因此内存中实际看到的是 78 56 34 12。

#
★★★

5. 解释为何 x86/x64 是 LE、PowerPC(老)多为 BE,ARM 可配?

解释为何 x86/x64 采用小端序、老 PowerPC 多为大端序、而 ARM 可以配置字节序?

  • 字节序的历史与工程选择
  • 各 ISA 的字节序策略
  • 字节序对软件兼容性与生态的影响

x86 从 8086 起就采用小端序,当时 Intel 认为小端序便于从低位开始逐字节处理,配合整数加法进位从低位开始更自然,且长期沿用形成兼容性生态。老 PowerPC 沿用了 IBM 的大端传统(IBM 大型机多采用大端,便于网络/协议处理)。ARM 的字节序是可配置的(BE-8/BE-32 或 LE),因为 ARM 需要同时服务不同生态(早期手机通信栈用大端、嵌入式 Linux 用小端),但现代 ARM(AArch64)实际已固定为小端,只在理论上支持配置。

字节序选择没有绝对优劣,更多是历史与生态的产物。x86 小端、PowerPC 大端、ARM 可配,体现了同一体系结构属性在不同厂商/时代决策下的差异。

#
★★★

6. 解释大端序(Big-Endian)与小端序(Little-Endian)的差异?

解释大端序(Big-Endian)与小端序(Little-Endian)的根本差异?

  • 字节序定义
  • 高低字节与地址的关系
  • 对读写/网络传输的影响

大端序把多字节数值的最高有效字节(MSB)存放在最低地址,小端序把最低有效字节(LSB)存放在最低地址。两者仅影响"字节在内存中的排列顺序",但不影响数值本身的大小。读取时只要与写入采用一致的字节序,就能得到正确值。差异体现在:跨机器交换数据(如网络协议、文件格式)时,如果双方字节序不同,必须进行转换;调试时小端序的字节流看起来是"倒着"的。大端序与人类阅读顺序一致且便于网络协议处理,小端序便于从低位开始运算和地址递增转换。

字节序是"数据表示"层面的基础概念,是理解网络字节序、文件格式、跨平台通信的前提。核心是"字节的摆放顺序"而非"位序"。

#
★★★

7. 解释网络字节序为何被定义为 BE?引用 RFC 1700 等?

解释网络字节序为何被定义为大端序(Big-Endian),并引用 RFC 1700 等依据?

  • 网络字节序约定
  • RFC 1700 相关内容
  • 大端在网络协议中的优势

网络字节序被定义为大端序,是因为最早的互联网协议(如 ARPANET、TCP/IP)由接入大端主机的工程团队制定,RFC 1700("Assigned Numbers")明确记载"传输协议中二进制整数的字节顺序是大端序(MSB first)",并被 RFC 791(IP)、RFC 793(TCP)等协议头定义所采用。大端序使得网络协议头中的多字节字段(如端口号、长度、校验和)在抓包时按自然书写顺序显示,便于阅读和调试,也符合当时主流主机的字节序。这一约定被标准化,成为所有网络协议必须遵守的规则。

网络字节序不是"技术上必须大端",而是历史选择并被标准化。它要求所有主机在发送前把主机序转成网络序(大端),接收后转回主机序,从而保证异构主机间通信正确。

#
★★★

8. 为何图像文件 BMP 是 LE、网络包 TCP/IP 头是 BE?

为何图像文件 BMP 采用小端序、而网络包 TCP/IP 头采用大端序?

  • 文件格式与网络协议字节序的来源
  • BMP 的 Windows 出身
  • 网络字节序的标准化

BMP 是微软为 Windows 平台定义的图像格式,而 Windows 运行在 x86(小端)处理器上,因此 BMP 文件头中的多字节字段(如像素偏移、宽高、位深)按小端序存储,形成平台的"原生字节序"。而 TCP/IP 头是网络协议,必须跨无数异构主机交换,因此采用被 RFC 标准化的统一大端序(网络字节序),保证任何机器都能正确解析。本质区别:文件格式可以服务于单一平台(小端即可),网络协议必须服务全球异构主机(必须统一为大端)。

字节序选择取决于"谁消费这份数据"。文件格式面向特定平台可用平台原生序;网络协议面向异构网络必须统一。这就是 BMP 小端、TCP/IP 头大端的原因。

#
★★★

9. 把 IEEE 754 单精度 1.0 写入二进制文件后用不同字节序读取,结果如何?

把 IEEE 754 单精度 1.0 写入二进制文件后,用不同字节序读取,结果会如何?

  • 浮点数的二进制位模式
  • 字节序对浮点解析的影响
  • 跨平台序列化浮点数时需统一字节序的实际意义

1.0 的 IEEE 754 单精度位模式是 0x3F800000(符号 0,指数 127,尾数 0)。若以小端序写入二进制文件,字节序列为 00 00 80 3F;若以同样小端序读回,得到 0x3F800000,即 1.0。但若用大端序读取这段字节,会把 0x0000803F 当作位模式,解析为约 4.6e-41 的极小数(subnormal),完全错误。同理,以某字节序写入、以另一字节序读取,浮点数数值会被破坏。结论:浮点数的字节序与整数一样由其二进制位模式决定,读写两端字节序必须一致。

浮点数不是"特殊不变的字节序列",它同样受字节序影响。写入文件或网络时必须明确并统一字节序,否则读出的数值荒谬。这是跨平台序列化浮点数的常见坑。

#
★★★

10. 设计一个示例用 ntohs 读取网络端口号 0x0050?

设计一个示例,用 ntohs 读取网络端口号 0x0050?

  • ntohs 的 16 位版本
  • 端口号在内存中的主机序/网络序转换
  • 0x0050 对应十进制 80(HTTP 端口)的换算

端口号在 TCP/UDP 头中占用 16 位,以网络字节序(大端)存储。ntohs 专门用于把网络字节序的 16 位值转换为主机字节序。0x0050 是十进制 80,即 HTTP 端口。示例:从收到的 TCP 头结构中取出端口字段(网络序),用 ntohs 转换为主机序后判断是否为 80。在小端机器上,网络中按大端传输的字节 00 50 直接作为 uint16_t 读取会得到 0x5000(即 20480),调用 ntohs 反转字节后得到 0x0050,其数值意义就是 80。

端口号是典型的"以网络字节序传输、主机需用 ntohs 转换"的字段。0x0050 大端字节序为 00 50,解析为 80;若不进行转换,小端主机直接读会得到 0x5000=20480,错误。

#include <arpa/inet.h>
#include <stdint.h>
#include <stdio.h>
int main() {
    uint16_t navail = 0x0050;      // 网络序的端口 80
    uint16_t host = ntohs(navail); // 转换为主机序
    printf("port on wire = %04x, host value = %u\n", navail, host);
    return 0;
}
#
★★★

11. IEEE 754 单精度与双精度的位布局分别是什么?符号、指数、尾数各占几位?

IEEE 754 单精度与双精度的位布局分别是什么?符号、指数、尾数各占多少位?

  • 单精度 32 位布局
  • 双精度 64 位布局
  • 偏置指数

单精度(float)共 32 位:符号 1 位、指数 8 位(偏置 127)、尾数 23 位。双精度(double)共 64 位:符号 1 位、指数 11 位(偏置 1023)、尾数 52 位。两者都采用"1 位符号 + 指数 + 尾数"布局,指数用偏置表示(单精度存储的指数 = 真实指数 + 127,双精度 = 真实指数 + 1023),尾数隐含最高位 1(normal 数),因此实际有效精度为 24 位(约 7 位十进制)和 53 位(约 15-16 位十进制)。

这是 IEEE 754 最基础的位布局。偏置指数使得 0 的指数表示全 0,便于大小比较;隐含 1 位数节省一位有效精度。记住"1/8/23"与"1/11/52"即可。

#
★★★

12. IEEE 754 标准的 RNE(round-to-nearest-even)舍入在 0.5 边界为何舍入到偶数?

IEEE 754 标准的 RNE(round-to-nearest-even)舍入在 0.5 边界时为何舍入到偶数?

  • RNE 舍入规则
  • 0.5 边界的处理
  • 避免统计偏差

RNE(round-to-nearest-even)是 IEEE 754 的默认舍入模式:舍入到最接近的可表示值,当恰好处于两个候选值正中间(如 0.5 边界)时,选择"偶数尾数"(最低有效位为 0)的那个。原因是避免系统性的统计偏差:如果固定向上或向下舍入,大量落在边界上的数会偏向同一方向,长时间累积产生系统性误差。舍入到偶数使边界值在两个方向间交替,长期统计期望上误差相互抵消,更公平。

这是一道考察"为何默认用 RNE"的问题。RNE 在"最接近"与"偶数"两个规则下保证了无偏舍入,是二进制浮点舍入的标准做法。

#
★★★

13. 在 IEEE 754 单精度中,指数全 0、全 1 的特殊编码分别表示什么?

在 IEEE 754 单精度中,指数全 0 与指数全 1 的特殊编码分别表示什么?

  • 指数全 0 的意义
  • 指数全 1 的意义
  • 特殊值编码

单精度指数域 8 位。指数全 0(0x00)表示两类特殊值:当尾数全 0 时表示 ±0(符号位决定正负零);当尾数非 0 时表示 subnormal(非规格化)数,用于表示更靠近 0 的极小值。指数全 1(0xFF)表示另两类特殊值:当尾数全 0 时表示 ±Infinity(无穷大);当尾数非 0 时表示 NaN(非数值)。因此正常数(normal)的指数范围为 1 到 254(真实指数 -126 到 +127),全 0 与全 1 被保留给特殊值。

指数编码的"两端"被保留用于特殊值,是 IEEE 754 设计的核心技巧。理解了全 0/全 1 的含义,就理解了 0、subnormal、Inf、NaN 的来源。

#
★★★

14. 把 0xC1800000(IEEE 754 单精度)解析为十进制数?

把 IEEE 754 单精度十六进制 0xC1800000 解析为十进制数?

  • 位模式拆分
  • 偏置指数还原
  • 计算数值

0xC1800000 二进制为 1100 0001 1000 0000 0000 0000 0000 0000。符号位 = 1(负数)。指数域 = 1000 0011(8 位)即 0x83 = 131,真实指数 = 131 - 127 = 4。尾数域 = 000...000(全 0),隐含 1,因此尾数 = 1.0。数值 = -1.0 × 2^4 = -16。所以 0xC1800000 表示 -16。

解析步骤固定:拆符号 → 拆指数(减偏置)→ 拆尾数(加隐含 1)→ 组合计算。0xC1800000 的符号位 1、指数 131、尾数 0,直接得到 -16。

#
★★★

15. 把十进制 6.625 用 IEEE 754 单精度表示,按符号/指数/尾数拆分并写出十六进制位模式?

把十进制 6.625 用 IEEE 754 单精度表示,按符号/指数/尾数拆分并写出十六进制位模式?

  • 十进制转二进制浮点
  • IEEE 754 编码
  • 十六进制位模式

6.625 = 6 + 0.625 = 110.101(二进制)。规范化为 1.10101 × 2^2。符号位 = 0(正数)。指数真实值 = 2,存储指数 = 2 + 127 = 129 = 0b10000001。尾数 = 10101(去掉隐含的 1),后补零到 23 位:1010 1000 0000 0000 0000 000。组合:符号 0 | 指数 10000001 | 尾数 10101000000000000000000 = 0 10000001 10101000000000000000000。按 8 位分组:0100 0000 1101 0100 0000 0000 0000 0000 = 0x40D40000。

固定流程:转二进制 → 规范化到 1.xxx × 2^e → 编码符号/指数(+偏置)/尾数(去隐含 1)。6.625 是能精确表示的二进制小数,因此位模式精确。

#
★★★

16. 给定十六进制 0x7FC00000,写出其类型(NaN/Inf/normal)与符号?

给定十六进制 0x7FC00000,写出其 IEEE 754 单精度类型(NaN/Inf/normal)与符号?

  • 指数域判断
  • 尾数判断
  • 符号判断

0x7FC00000 二进制为 0111 1111 1100 0000 0000 0000 0000 0000。符号位 = 0(正)。指数域 = 1111 1111(全 1)。尾数域最高位为 1(非全 0),因此这是 NaN(不是 Inf,因为 Inf 需要尾数全 0)。由于符号位为 0,可写作 +NaN(NaN 的符号通常不参与运算,但可记录)。

通过指数全 1 + 尾数非 0 判定为 NaN,符号位 0 表示正 NaN。区分 NaN 与 Inf 的关键是"尾数是否全 0"。

#
★★

17. 解释为何 IEEE 754 用偏置指数(单精度 +127、双精度 +1023),而不是补码或符号-绝对值?

解释为何 IEEE 754 用偏置指数(单精度 +127、双精度 +1023),而不是补码或符号-绝对值?

  • 偏置指数的好处
  • 全 0/全 1 保留
  • 数值比较的便利

偏置指数(biased exponent)设计为:在指数域中存储"真实指数 + 偏置",使得所有正常浮点数的指数域都是非负的、单调递增的。好处:一是存储的指数域全 0 与全 1 可以自然保留给特殊值(0、subnormal、Inf、NaN),无需额外标志位;二是正浮点数的位模式(符号+指数+尾数)按无符号整数比较的大小顺序,恰好与浮点数值大小顺序一致,硬件可以直接用整数比较器比较浮点大小,简化比较逻辑;三是避免了补码中 +0 与 -0 表示重复的问题(由符号位处理)。

偏置指数让"指数域呈单调递增的无符号数",从而让浮点位模式有序、便于比较,并让两端编码留给特殊值。这是 IEEE 754 规范化设计的精髓。

#
★★

18. 为何 IEEE 754 把 +0 与 -0 区分开来?两者的比较运算结果为何相等?

为何 IEEE 754 把 +0 与 -0 区分开来?两者的比较运算结果为何相等?

  • 有符号零的来源
  • IEEE 754 有符号零语义
  • 相等比较与符号保留

IEEE 754 区分 +0 与 -0 是因为浮点运算会产生带符号的零(如 1/负无穷 = -0,或 -0 参与运算),保留符号位可以让某些数学性质(如 1/(-0) = -Inf)保持正确,也便于区分上下溢方向。但在比较运算中,+0 与 -0 被规定为相等(+0 == -0 为真),因为从数值意义上两者都等于 0,绝大多数算法希望它们相等。符号位只在需要时体现(如 1/x 的符号、log 的符号)。

"有符号零"是 IEEE 754 的一个精巧设计:数值上相等,但符号位保留,用于维持数学连续性。比较时视作相等,运算时保留符号。

#
★★

19. 定点数 Q15/Q31 各自表示的小数范围与精度?

定点数 Q15、Q31 各自能表示的小数范围与精度分别是多少?

  • Q 格式定点数
  • Q15/Q31 的位宽与精度
  • 定点值 = 存储整数 × 2^-n 的换算公式

Q15 用 16 位(1 位符号 + 15 位小数)表示,范围约为 -1 到 +0.99997,步长(精度)为 2^-15 ≈ 3.05e-5。Q31 用 32 位(1 位符号 + 31 位小数)表示,范围约为 -1 到 +0.9999999995,步长(精度)为 2^-31 ≈ 4.66e-10。Q 格式用"n 位小数"表示,值 = 存储整数 × 2^-n,精度即 2^-n。

Q 格式是 DSP 常用的定点格式,用固定位数表示 -1 到 1 之间的数。Q15/Q31 的精度分别由 15/31 位小数决定,用于 DSP 中替代浮点以节省硬件。

#
★★

20. 定点数 Q7.8 能表示的范围与步长分别是多少?

定点数 Q7.8 能表示的范围与步长分别是多少?

  • Qm.n 格式
  • 整数/小数位分配
  • 范围与步长计算

Q7.8 表示:1 位符号 + 7 位整数 + 8 位小数,共 16 位。整数部分 7 位,范围 -128 到 +127(含符号的总整数值最大 2^7-1=127);小数部分 8 位,步长为 2^-8 = 1/256 ≈ 0.00390625。因此可表示范围约为 -128 到 127.99609375,步长为 0.00390625。

Qm.n 中 m 为整数位、n 为小数位。范围主要由整数位决定,步长由小数位决定(2^-n)。Q7.8 是 16 位定点中整数/小数分配较均衡的格式。

#
★★

21. 把十进制 0.5 表示为 Q15(int16)时,如何写出位模式?

把十进制 0.5 用 Q15(int16)定点表示,写出位模式?

  • Q15 编码
  • 小数转定点
  • 定点转存 = 数值 × 2^小数位 的计算

Q15 用 16 位表示,值 = 存储整数 × 2^-15。要表示 0.5,存储整数 = 0.5 × 2^15 = 16384 = 0x4000。位模式为 0100 0000 0000 0000(0x4000)。验证:16384 × 2^-15 = 0.5。符号位为 0,表示正数。

定点转存 = 数值 × 2^小数位。0.5 恰好是 2^-1,0.5 × 32768 = 16384,正好 0x4000。

#
★★

22. 给定 1.234567 元(精度 6 位小数),用 DECIMAL(19,4) 存储会损失几位?

给定 1.234567 元(6 位小数精度),用 DECIMAL(19,4) 存储会损失几位?

  • DECIMAL(p,s) 语义
  • 小数位截断
  • 小数位从 6 位降到 4 位所损失位数的计算

DECIMAL(19,4) 表示小数部分保留 4 位。1.234567 有 6 位小数,存入 DECIMAL(19,4) 后小数部分被截断或四舍五入到 4 位,即变为 1.2346(四舍五入)或 1.2345(截断),损失了 2 位小数精度(后两位 67 被丢弃)。DECIMAL(19,4) 对 1.234567 的整数部分无影响,但小数位从 6 位降到 4 位。

DECIMAL(p,s) 的 s 决定小数位数,超出 s 的小数位会被舍入/截断。精度不足就损失精度,这是设计表结构时要注意的点。

#
★★

23. 解释 IEEE 754 中 0.1 + 0.2 ≠ 0.3 的根本原因与定点方案如何避免?

解释 IEEE 754 中 0.1 + 0.2 ≠ 0.3 的根本原因,以及定点方案如何避免该问题?

  • 二进制无法精确表示部分十进制小数
  • 舍入误差累积
  • 定点方案

根本原因是 0.1 和 0.2 在二进制中都是无限循环小数,无法被 IEEE 754 双精度精确表示,只能近似存储。近似值相加后再与 0.3 的近似值比较,会出现 0.30000000000000004 ≠ 0.3 的误差。定点方案(如 DECIMAL、以分为单位的整数)用整数或固定小数位表示,0.1 和 0.2 在十进制定点下可精确表示(如 10 分、20 分),相加得 30 分,精确等于 0.3,从根源上避免了二进制近似误差。

这是浮点误差最著名的例子。避免方法:比较时用容差(epsilon),或改用十进制定点/整数表示金额。定点方案因为"小数位固定、十进制精确",能保证如 0.1+0.2=0.3。

#
★★

24. 解释为何金融系统偏好定点小数(DECIMAL)而非 IEEE 754 双精度?

解释为何金融系统偏好定点小数(DECIMAL)而非 IEEE 754 双精度?

  • 浮点误差
  • 金融精度要求
  • DECIMAL 的精确性

金融系统处理金额,要求精确的十进制结果,不能有二进制舍入误差(如 0.1+0.2≠0.3)。IEEE 754 双精度是二进制表示,无法精确表达大量十进制小数,长期累积误差会导致账目不平、审计问题。DECIMAL(定点小数)用十进制数字存储,每个数位精确表示,四舍五入可控、可预测,符合金融的"精确到分"要求和会计准则(如四舍五入到分)。此外 DECIMAL 运算规则明确、可审计,若用浮点则需额外的误差处理逻辑。

金融的核心诉求是"精确与合规",二进制浮点天然无法完全满足。DECIMAL 提供精确定点,牺牲部分速度和内存换取确定性,是金融数据库字段的标准选择。

#
★★

25. 设计用整数(最小单位 1 分)存储金额的表结构,列出优缺点?

设计一张用整数(最小单位 1 分)存储金额的表结构,并列出其优缺点?

  • 整数存金额
  • 分/毫换算
  • 优缺点分析

金额以"分"为单位存为 BIGINT 或 INTEGER,例如:CREATE TABLE orders (id BIGINT, amount_cents BIGINT NOT NULL); 值为 123456 表示 1234.56 元。优点:整数值精确,无浮点误差;运算(加减乘)是精确整数运算,速度快;存储与简单。缺点:需要开发者在代码中做"分↔元"换算,容易出错;除法/比例运算可能产生小数,需自行处理舍入;若金额需要更细粒度(如毫),需约定最小单位;跨系统展示时需格式化。

整数存最小单位是"以分为单位"的经典方案,本质是定点整数。优点是精确、快速,缺点是开发需自行管理换算与舍入规则。

CREATE TABLE orders (
  id          BIGINT PRIMARY KEY,
  amount_cents BIGINT NOT NULL,   -- 金额,单位:分
  created_at  TIMESTAMP NOT NULL
);
#
★★

26. 给定 999999999999.9999(DECIMAL(18,4)),能存入吗?边界如何?

给定 999999999999.9999(DECIMAL(18,4)),能存入吗?边界如何?

  • DECIMAL(p,s) 容量
  • 整数位计算
  • 整数位上限 = p - s 的判定

DECIMAL(18,4) 表示总 18 位、小数 4 位,因此整数部分最多 18-4 = 14 位。999999999999.9999 有 12 位整数(999999999999)和 4 位小数,整数部分 12 ≤ 14,能存入。最接近边界的最大可存值为 99999999999999.9999(14 位整数 + 4 位小数),超过 14 位整数就会溢出报错。

DECIMAL(p,s) 的整数位上限 = p - s。判断能否存入的关键是整数位数 ≤ p-s 且小数位数 ≤ s。999999999999.9999 满足条件。

#
★★

27. ASCII 与 Latin-1(ISO-8859-1)的位宽与覆盖范围?

ASCII 与 Latin-1(ISO-8859-1)的位宽与覆盖范围分别是什么?

  • ASCII 7 位
  • Latin-1 8 位
  • 覆盖字符范围

ASCII 使用 7 位(0x00-0x7F),覆盖 128 个字符:控制字符(0x00-0x1F)、可打印的英文大小写字母、数字、标点。Latin-1(ISO-8859-1)使用 8 位(0x00-0xFF),覆盖 256 个字符:前 128 个与 ASCII 相同,后 128 个(0x80-0xFF)补充了拉丁字母的重音字符(如 é、ü)、货币符号及一些标点。Latin-1 是 ASCII 的 8 位扩展,主要覆盖西欧语言。

ASCII 是 7 位字符集,Latin-1 是 8 位扩展,二者兼容(Latin-1 的 0x00-0x7F 与 ASCII 完全一致)。这是字符集发展史上的关键衔接点。

#
★★

28. UTF-16 中 U+4E2D(中)的编码是 0x4E 0x2D 还是 0x2D 0x4E?

UTF-16 中 U+4E2D("中")的编码是 0x4E 0x2D 还是 0x2D 0x4E?

  • UTF-16 编码
  • 字节序
    • BOM(FF FE / FE FF)对字节序的标记

这取决于字节序。U+4E2D 在 BMP 内,直接用 2 字节表示。大端(UTF-16BE)下字节为 0x4E 0x2D;小端(UTF-16LE)下字节为 0x2D 0x4E。文件通常用 BOM(FF FE 表示 LE,FE FF 表示 BE)指明字节序。若不考虑字节序,仅说"编码值",则码元是 0x4E2D;若问字节序列,则需区分大小端。

"0x4E 0x2D 还是 0x2D 0x4E"的答案看字节序。UTF-16 码元 0x4E2D 在大端先存高字节 0x4E,小端先存低字节 0x2D。

#
★★

29. UTF-16 中 😀(emoji)的字节序列与 UTF-8 字节序列差异?

UTF-16 与 UTF-8 中 😀(U+1F600)的字节序列分别是什么,有何差异?

  • U+1F600 在 BMP 之外
  • 代理对编码
  • UTF-8 4 字节编码

😀 的码点是 U+1F600,在 BMP 之外(辅助平面),因此 UTF-16 需要用代理对表示:高位代理 0xD83D、低位代理 0xDE00。UTF-16LE 字节序列为 3D D8 00 DE;UTF-16BE 为 D8 3D DE 00。UTF-8 中 U+1F600 用 4 字节编码:F0 9F 98 80。差异:UTF-16 用代理对(2 个码元共 4 字节),UTF-8 用 4 字节序列,编码方式完全不同但字节数都是 4。

这是 BMP 外字符的典型例子。UTF-16 用代理对(surrogate pair),UTF-8 用 4 字节序列。两者字节数都是 4,但字节内容不同。

#
★★

30. UTF-8 中字节 0xC0 与 0xC1 为何被禁止?

UTF-8 中字节 0xC0 与 0xC1 为何被禁止?

  • UTF-8 编码规则
  • 过长编码
  • 安全考虑

0xC0 与 0xC1 是 2 字节序列的 leading byte(前缀 110),但对应的码点范围 0x00-0x7F 本可用单字节表示。若用 0xC0/0xC1 开头,就构成"过长编码"(overlong encoding),会产生同一个码点的多种表示,破坏 UTF-8 的唯一性,可能被用于绕过安全过滤(如 ASCII 中的特殊字符被编码成多字节形式)。因此 UTF-8 规范禁止 0xC0 与 0xC1 作为首字节,保证每个码点只有唯一一种编码。

过长编码是 UTF-8 安全性的关键问题。禁止 0xC0/0xC1 可防止"同一码点多种表示"导致的绕过攻击,是规范为安全而设的约束。

#
★★

31. UTF-8 编码中 0xE4 0xB8 0xAD 对应的 Unicode 码点是多少?

在 UTF-8 编码中,字节序列 0xE4 0xB8 0xAD 对应的 Unicode 码点是多少?

  • UTF-8 解码
  • 3 字节序列
  • 前缀位剥离与码点拼装

0xE4 是 3 字节序列的 leading byte(前缀 1110),去掉前缀 1110 得 4 位有效数据 = 0x4。0xB8 是 continuation byte(前缀 10),去掉前缀得 6 位 = 0x38。0xAD 是 continuation byte,去掉前缀得 6 位 = 0x2D。组合:0x4 << 12 | 0x38 << 6 | 0x2D = 0x4000 | 0xE00 | 0x2D = 0x4E2D。所以 0xE4 0xB8 0xAD 对应码点 U+4E2D,即汉字"中"。

UTF-8 解码:leading byte 提供码点高位,continuation 提供低位。0xE4 0xB8 0xAD 是"中"的经典 UTF-8 编码。

#
★★

32. 解释 UTF-8 单字节区 0x00-0x7F 与多字节前缀 0xC0-0xF7、0x80-0xBF 的规则?

解释 UTF-8 中单字节区 0x00-0x7F 与多字节前缀 0xC0-0xF7、0x80-0xBF 的规则?

  • UTF-8 编码结构
  • 前缀字节规则
  • continuation 规则

UTF-8 的字节分为三类:0x00-0x7F 是单字节区,直接表示 ASCII 码点(leading byte 前缀为 0),与 ASCII 完全兼容。0xC0-0xF7 是多字节序列的 leading byte:0xC0-0xDF 表示 2 字节序列(前缀 110),0xE0-0xEF 表示 3 字节(前缀 1110),0xF0-0xF7 表示 4 字节(前缀 11110),前缀位决定后续 continuation 的个数。0x80-0xBF 是 continuation byte(前缀 10),只能出现在 leading byte 之后,用于携带剩余码点位。规则保证 UTF-8 自同步:从任意字节能判断是单字节、leading 还是 continuation。

UTF-8 通过前缀位区分字节类型,实现自同步与 ASCII 兼容。leading byte 的 0 个数决定序列长度,continuation 固定为 10 开头。

#
★★

33. 解释 UTF-8 的 leading byte 与 continuation byte 检测函数?

解释如何编写检测 UTF-8 leading byte 与 continuation byte 的函数?

  • 前缀位判断
  • 位运算
  • 各字节类型对应的掩码判定(0xC0/0xE0/0xF0/0xF8)

判断一个字节是 continuation byte 的方法:检查最高位是否为 10 开头,即 (b & 0xC0) == 0x80。判断 leading byte 的序列长度:看 (b & 0xE0) == 0xC0 是 2 字节,(b & 0xF0) == 0xE0 是 3 字节,(b & 0xF8) == 0xF0 是 4 字节;若 (b & 0x80) == 0 则是单字节 ASCII。通过位掩码即可可靠识别字节类型。

UTF-8 自同步特性使检测只需掩码比较。continuation 固定 10 开头,leading 的前缀决定长度,非常高效。

int is_continuation(unsigned char b) { return (b & 0xC0) == 0x80; }
int seq_len(unsigned char b) {
    if ((b & 0x80) == 0) return 1;              // ASCII
    if ((b & 0xE0) == 0xC0) return 2;           // 110xxxxx
    if ((b & 0xF0) == 0xE0) return 3;           // 1110xxxx
    if ((b & 0xF8) == 0xF0) return 4;           // 11110xxx
    return -1;                                  // 非法
}
#
★★

34. 解释 Big-endian UTF-16 BOM(0xFE 0xFF)与 Little-endian BOM(0xFF 0xFE)的字节序识别?

解释 UTF-16 的 BOM(Byte Order Mark)如何识别字节序:大端 BOM 0xFE 0xFF 与小端 BOM 0xFF 0xFE?

  • BOM 概念
  • UTF-16 BOM 值
  • 字节序识别

BOM(字节序标记)是置于文件开头的 U+FEFF 字符编码。在 UTF-16 中,若以小端序存储 U+FEFF(码元 0xFEFF),字节为 FF FE;若以大端序存储,字节为 FE FF。因此读取文件开头两个字节:若为 FF FE 则文件是 UTF-16LE,若为 FE FF 则文件是 UTF-16BE。系统据此确定后续所有码元的字节序。BOM 本身是 U+FEFF,在文件中也被称为"零宽不换行空格",但作为 BOM 时主要起字节序标记作用。

BOM 是 UTF-16 文件自描述字节序的机制。FF FE 对应 LE,FE FF 对应 BE,与字节序本身一致:LE 先存低字节。

#
★★

35. NFD 后的 é 字符(U+0065 U+0301)字节数与 NFC 的 U+00E9 字节数?

NFD 规范化后的 é(码点序列 U+0065 U+0301)在 UTF-8 中的字节数,与 NFC 的 U+00E9 字节数分别是多少?

  • NFD/NFC 规范化
  • é 的两种表示
  • UTF-8 字节数

NFD(分解)把 é 分解为 e(U+0065)+ 组合重音符(U+0301),两个码点。U+0065 是 ASCII,UTF-8 占 1 字节;U+0301 在 U+0300-U+03FF 区间,UTF-8 占 2 字节(0xCC 0x81),因此 NFD 形式共 3 字节。NFC(组合)把 é 表示为单一码点 U+00E9,U+00E9 在 U+0080-U+07FF 区间,UTF-8 占 2 字节(0xC3 0xA9)。所以 NFD 为 3 字节,NFC 为 2 字节。

é 有两种合法 Unicode 表示:预组合(U+00E9)与分解(e + U+0301)。UTF-8 字节数因码点个数与码点范围不同而不同:NFD 3 字节、NFC 2 字节。

#
★★

36. NFD、NFC、NFKD、NFKC 四种规范化形式的区别?

解释 NFD、NFC、NFKD、NFKC 四种 Unicode 规范化形式的区别?

  • 分解 vs 组合
  • 兼容性 vs 标准等价
  • 各规范化形式的典型应用场景

四种规范化形式由两个维度组合而成:分解(D)vs 组合(C),以及标准等价(标准)vs 兼容等价(K)。NFC(组合标准):先分解再按规范重组,结果是预组合字符(é 为 U+00E9)。NFD(分解标准):完全分解为基字符+组合符(é 为 e+U+0301)。NFKC(组合兼容):先做兼容分解(把兼容字符如全角、连字拆成标准形式)再重组。NFKD(分解兼容):做兼容分解,不重组。K 形式的特征是"兼容性映射",会把视觉等价但非标准等价的字符统一(如全角A→A、连字fi→fi)。

NFC/NFD 处理标准等价(同一字符的合法表示),NFKC/NFKD 额外处理兼容等价(视觉等价但编码不同的字符)。K 常用于搜索/规范化输入。

#
★★

37. 为何 emoji 👍(🏻 选择肤色)由 2 个码点组成?

为何 emoji 👍🏻(可选肤色)由 2 个码点组成?

  • emoji 肤色修饰符
  • 扩展字素簇
  • 组合模型避免为每种肤色单独定义码点

👍🏻 由"基础 emoji 👍(U+1F44D)"+"肤色修饰符 U+1F3FB(🏻 浅肤色)"两个码点组成。Unicode 的 emoji 肤色系统用统一的肤色修饰符(U+1F3FB 到 U+1F3FF 五种肤色)附加到基础 emoji 后,表示不同肤色。这两个码点相邻形成"扩展字素簇"(extended grapheme cluster),被处理为单个用户可感知字符(显示为彩色大拇指)。这是 Unicode 用"组合"而非"预定义每个肤色字符"来扩展 emoji 的机制。

若为每种肤色单独定义码点,会爆炸式增长。Unicode 用"基础 emoji + 肤色修饰符"的组合模型,节省码点并保持可扩展性。

#
★★

38. 给定字符串 é + ́ + ́(base+两个重音符),NFD 与 NFC 各是什么码点序列?

给定字符串 é + ◌́ + ◌́(基字符加两个重音符),NFD 与 NFC 分别是什么码点序列?

  • 组合字符规范化
  • 组合顺序
    • NFC 组合规则与无法再组合的情形

该字符串由 e(U+0065)+ 重音符 U+0301 + 重音符 U+0301 组成。NFD 保持分解形式:e(U+0065)+ U+0301 + U+0301(两个组合符保持独立)。NFC 会尝试组合:基字符 e 与第一个 U+0301 组合成预组合 é(U+00E9),但第二个 U+0301 无法与被组合后的 é 再组合(因为 U+00E9 已是最短组合),因此 NFC 结果为 U+00E9 + U+0301。即 NFC 后仍是 2 个码点(一个组合字符 + 一个多余重音符)。

NFC 会尽量把可组合的基字符与组合符合并,但多余的组合符无法再合并。这展示了组合规则:一个字符通常只能组合一个组合符。

#
★★

39. 解释 Unicode Hangul 组合算法中的 L+V+T 合成规则

解释 Unicode 的 Hangul(韩文)合成算法:L+V+T 合成规则?

  • 韩文音节结构
  • 合成算法
  • 码点计算

韩文音节由初声(L,Leading)、中声(V,Vowel)、终声(T,Trailing)三部分组成。Unicode 提供合成算法:给定 L、V、T 的码点,可计算合成音节码点。公式:音节码点 = SBase + (L - LBase) * NCount + (V - VBase) * TCount + (T - TBase),其中 SBase = 0xAC00,LBase = 0x1100,VBase = 0x1161,TBase = 0x11A7,TCount = 28,NCount = 588。T 为 0 表示无终声。该算法使 11172 个韩音节通过 3 部分组合动态生成,无需逐一编码。

韩文音节是"组合式"的典范,Unicode 用算法而非逐个码点表示,节省大量码点并支持规范化。L+V 组合成音节,L+V+T 再组合成带终声音节。区分 L 区块(0x1100-0x11FF)与已合成音节(0xAC00-0xD7A3)。

#
★★

40. UTF-8 中 😀(U+1F600)的字节数与 char32_t 容量?

U+1F600(😀)在 UTF-8 中占多少字节?char32_t 能否容纳?

  • UTF-8 4 字节编码
  • char32_t 容量
  • 码点区间与 UTF-8 字节数的对应关系

U+1F600 在 0x10000-0x10FFFF 区间,UTF-8 占 4 字节(F0 9F 98 80)。char32_t 是 32 位整数类型,能直接容纳最多 0x10FFFF 的码点,因此 U+1F600 可以直接存入一个 char32_t(值为 0x1F600)。注意:char32_t 存储的是"码点"而非"UTF-8 字节",因此一个 char32_t 对应该 emoji 的码点,而 UTF-8 需要 4 个字节。

UTF-8 的字节数由码点范围决定(BMP 外 4 字节)。char32_t 是 32 位,能容纳全部 Unicode 码点(0x10FFFF),故一个 char32_t 足以表示 U+1F600。

#
★★

41. 解释“man + combining tilde”(NFD) → mañ(NFC)变体?

解释"man + combining tilde"(NFD 形式)在 NFC 规范化后变为 mañ(NFC 变体)的过程?

  • NFD/NFC 转换
  • n + 组合波浪号
    • n + U+0303 存在预组合字符 ñ(U+00F1)的合并

字符串 "man" + 组合波浪号(U+0303)在 NFD 下为码点序列 m(U+006D)、a(U+0061)、n(U+006E)、U+0303。NFC 规范化会尝试组合:基字符 n(U+006E)与组合波浪号 U+0303 在 Unicode 中定义为预组合字符 ñ(U+00F1),因此 NFC 会把 n + U+0303 组合成单一码点 U+00F1,得到 m、a、U+00F1,即 "mañ"。m 和 a 没有组合符,保持不变。

n + 组合波浪号有预组合形式 ñ,NFC 会合并为一个码点。这展示了 NFC 的"组合"行为如何把分解序列归并为预组合字符。

#
★★

42. 解释 Unicode 码点(codepoint)、字素簇(grapheme cluster)、字形(glyph)三者的层次差异?

解释 Unicode 码点(codepoint)、字素簇(grapheme cluster)、字形(glyph)三者的层次差异?

  • 码点概念
  • 字素簇概念
  • 字形概念

码点(codepoint)是 Unicode 给每个抽象字符分配的唯一编号(如 U+4E2D),是"字符层"的标识。字素簇(grapheme cluster)是一个或多个码点组成的"用户可感知的最小字符单元",如 é(e+U+0301)、肤色 emoji(👍+U+1F3FB)是单个字素簇但由多个码点组成。字形(glyph)是图形界面上的具体视觉形状,同一码点在不同字体/字形变体下可呈现不同 glyph(如衬线/无衬线)。层次关系:码点是抽象的编码单位,字素簇是用户眼中的"字符",字形是渲染出的视觉图像。

三者是"编码层→用户感知层→渲染层"的层次。码点≠字形,字素簇把多个码点打包成用户感知的字符,是字符串处理、光标/索引操作的正确单位。

#

43. 解释 combining character(如 ́ 重音符)与 base character 关系?

解释 combining character(如重音符 ◌́)与 base character 的关系?

  • 组合字符概念
  • 基字符关系
  • 组合类(combining class)与字素簇的构成

combining character(组合字符)是不能独立显示、必须附着在基字符(base character)上的字符,如组合重音符 U+0301。它通过与基字符相邻(通常紧随其后)来修饰基字符,二者共同构成一个字素簇(如 e + U+0301 = é)。组合字符有自己的码点,但渲染时叠加在基字符上。Unicode 为组合字符定义了"组合类"(combining class),用于决定组合顺序与规范化。基字符与组合字符的关系是"被修饰"与"修饰"的关系。

组合字符是 Unicode 中"一个可见字符可以由多个码点构成"的机制,与预组合字符(单一码点)是等价表示。处理字符串时需注意将基字符+组合字符视为一个逻辑单元。

#

44. 在 Python struct.pack 中 < 与 > 前缀分别表示什么字节序?

在 Python struct.pack 中,< 与 > 前缀分别表示什么字节序?

  • struct 模块字节序
  • < 与 > 含义
  • 其他前缀(= 原生、! 网络序)的含义

在 Python struct 模块中,字节序前缀 < 表示小端序(little-endian),> 表示大端序(big-endian)。例如 struct.pack('>H', 0x1234) 打包为 12 34(大端),struct.pack('<H', 0x1234) 打包为 34 12(小端)。此外 = 表示按系统原生字节序与对齐,! 表示网络字节序(大端)。< 与 > 还会顺带关闭对齐(紧凑打包)。

struct 的 < 和 > 是控制字节序的标准前缀,! 是网络字节序别名(也即大端)。用它们可跨平台精确控制打包字节序。

import struct
print(struct.pack('>H', 0x1234))  # b'\x12\x34' 大端
print(struct.pack('<H', 0x1234))  # b'\x34\x12' 小端
#

45. 解释 NaN 的 payload(尾数部分非零)有何用途,举出 IEEE 754-2008 中规定的用法?

解释 NaN 的 payload(尾数部分非零)有何用途,并举例 IEEE 754-2008 中规定的用法?

  • NaN payload
  • qNaN/sNaN
  • 自定义信息

NaN 的尾数部分(payload)非零,可用于携带自定义信息。IEEE 754-2008 区分两类 NaN:quiet NaN(qNaN,尾数最高位为 1,静默传播,不触发异常)和 signaling NaN(sNaN,尾数最高位为 0,参与运算时触发无效操作异常)。payload 的其余位可用于编码诊断信息,如错误类型、数据类型、来源模块等。IEEE 754-2008 定义了 payload 的传播规则:运算结果 NaN 的 payload 通常取某个操作数的 payload,并规定了 payload 的保留与测试方法。实际用途包括:调试时标记"无效值来源"、JIT 中编码特殊值等。

NaN 不只表示"非数值",其 payload 提供了 22 位(单精度)可用于信息编码。qNaN/sNaN 区分 + payload 用法是 IEEE 754-2008 的重要扩展。

#

46. 解释 subnormal(denormal)数与 normal 数的分界点为何是 2^(-126) 与 2^(-1022)?

解释 subnormal(denormal)数与 normal 数的分界点为何是 2^(-126) 与 2^(-1022)?

  • normal 最小指数
  • subnormal 定义
  • 分界点

normal 数的指数域为 1 到偏置最大值(单精度真实指数 -126 到 +127),因此最小的 normal 数为 1.0 × 2^(-126)。小于该值的数无法用 normal 表示(指数域已是 1 的最小值),此时指数域为 0,进入 subnormal 区。subnormal 用"指数编码为 0、尾数不带隐含 1"的方式表示更小的数,最大值即接近 2^(-126),最小为 2^(-149)(单精度,尾数最低位 1)。同理双精度 normal 最小为 2^(-1022),subnormal 最小为 2^(-1074)。所以分界点 2^(-126)/2^(-1022) 是"指数域全 0 时带隐含 1 的 normal 最小"与"subnormal 区"的边界。

分界点由指数域的最小值决定:normal 至少要指数域=1(真实指数 -126),指数域=0 即进入 subnormal。subnormal 通过"无隐含 1、且尾数非零"表示更小的、等距步长的数。

#

47. 解释 bigint 在 JavaScript 中可表示的最大整数与最小整数?

解释 JavaScript 中 bigint 可表示的最大整数与最小整数?

  • BigInt 特性
  • 任意精度
  • 与 Number 的差异及 2^53 精确表示局限

JavaScript 的 BigInt 是任意精度整数类型,没有固定的最大或最小整数限制——理论上可以表示任意大的整数,仅受内存限制。与 Number 不同,Number 只能精确表示到 2^53-1(Number.MAX_SAFE_INTEGER),BigInt 则可以通过追加 n 或 BigInt() 表示任意大小整数(如 2n ** 1000n)。BigInt 与 Number 不能直接混合运算,需显式转换。因此"最大/最小整数"不存在固定上限,受运行环境内存约束。

BigInt 的本质是任意精度,因此没有 Number 那样的安全整数上限。这题考察的是一个常见误解:BigInt 没有固定最大值。

#

48. SQL 中 DECIMAL(p,s) 的 p 与 s 各代表什么含义?

SQL 中 DECIMAL(p,s) 的 p 与 s 各代表什么含义?

  • 精度 p
  • 标度 s
  • 整数位 = p - s 及 s ≤ p 的约束

DECIMAL(p,s) 中,p 是精度(precision),表示该数值总共的十进制数字位数(含整数部分和小数部分);s 是标度(scale),表示小数部分的位数。例如 DECIMAL(19,4) 表示总 19 位、小数 4 位,因此整数部分最多 19-4=15 位。约束:s 必须 ≤ p。p 决定数值的规模,s 决定小数精度。

p 是总位数,s 是小数位数,整数位 = p - s。这是理解 DECIMAL 存储与边界的基础。

#

49. 解释 BMP(基本多语言平面)与辅助平面(astral plane)的码点范围?

解释 BMP(基本多语言平面)与辅助平面(astral plane)的码点范围?

  • BMP 范围
  • 辅助平面范围
  • 平面概念

Unicode 码点空间为 0x0000-0x10FFFF,共 17 个平面(含 BMP 和 16 个辅助平面)。BMP(基本多语言平面)是第一个平面,码点范围 0x0000-0xFFFF,覆盖绝大多数常用字符(拉丁、CJK、常见符号等)。辅助平面(astral plane)是 BMP 之外的平面 U+10000-U+10FFFF,包含罕用字符、数学符号、emoji、CJK 扩展等。BMP 之外的码点在 UTF-16 中需要用代理对表示,UTF-8 需 4 字节。

BMP 是 0x0000-0xFFFF,辅助平面是 U+10000-U+10FFFF。区分 BMP 与辅助平面是理解 UTF-16 代理对、UTF-8 字节数的关键。

#

50. UTF-16 中代理对(surrogate pair)的计算公式?

解释 UTF-16 中代理对(surrogate pair)的计算公式?

  • 代理对编码
  • 高位/低位代理
  • 公式

对 BMP 外的码点 U(U+10000 到 U+10FFFF),先计算 V = U - 0x10000(范围 0 到 0xFFFFF,20 位)。高位代理(high surrogate)= D800 + (V >> 10)(取高 10 位),范围 D800-DBFF;低位代理(low surrogate)= DC00 + (V & 0x3FF)(取低 10 位),范围 DC00-DFFF。逆向:U = 0x10000 + ((high - D800) << 10) + (low - DC00)。例如 😀 U+1F600:V = 0x1F600-0x10000 = 0xF600,high = D800 + (0xF600>>10)=D800+0x3D=D83D,low = DC00 + (0xF600 & 0x3FF)=DC00+0x200=DE00,得到 D83D DE00。

代理对把 20 位的补码拆成两个 10 位部分,分别加到 D800 和 DC00 基址上。D800-DBFF 是高位代理,DC00-DFFF 是低位代理,二者形成的代理对唯一表示 BMP 外码点。

#

51. 为何"家庭"等 emoji 序列(成员之间用零宽连接符 ZWJ 连接)会被视为一个 grapheme(字形簇)而非多个字符?

为何"家庭"等 emoji 序列(成员之间用零宽连接符 ZWJ 连接)会被视为一个 grapheme 而非多个字符?

  • ZWJ 概念
  • emoji 序列
  • 字素簇规则

ZWJ(Zero Width Joiner,U+200D)是零宽连字符,用于连接多个 emoji 使其组合成一个复合 emoji。Unicode 的扩展字素簇(extended grapheme cluster)规则规定:当 ZWJ 出现在 emoji 之间时,ZWJ 前后的 emoji 与 ZWJ 一起被归入同一个字素簇(grapheme cluster),渲染时作为一个整体(如"👨👩👧👦"家庭 emoji)。这样多个码点被"粘合"成用户感知的单个字符,其内部码点仍可拆分,但作为文本编辑/光标/索引的逻辑单元是一致的。

ZWJ 本身不可见,但它在字素簇规则中扮演"连接"角色,使相邻 emoji 联合为一个 grapheme。这是 emoji 组合(职业、家庭、旗帜等)的 Unicode 机制。

#

52. 解释为何 U+2126(Ohm 符号)与 U+03A9(Omega)规范化后统一为 U+03A9?

解释为何 U+2126(Ohm 符号)与 U+03A9(Omega)规范化后统一为 U+03A9?

  • 兼容等价
  • 规范分解映射
  • 归一化

U+2126(Ohm 符号 Ω)与 U+03A9(希腊大写 Omega Ω)在视觉上几乎相同,且 U+2126 被 Unicode 定义为与 U+03A9 规范化等价(canonically equivalent),其规范分解映射为 U+03A9。因此 NFC/NFD 规范化会把 U+2126 归一化为 U+03A9,使同一字母只有一个规范表示。这避免了同一字符存在两个码点导致比较、搜索、索引不一致的问题,是 Unicode 消除"重复字符"的规范化机制。

类似"重复字符"(如 U+2126 与 U+03A9)通过规范分解映射统一。规范化的意义在于保证等价字符串有唯一表示,便于比较与检索。