ICS 与 8086 复习 03:补码 溢出与类型转换
FFH 是 255 还是 -1?7FH+01H 为什么可能有“溢出”却没有“进位”?这些题都在考一个共同点:位模式、解释规则和运算语义需要分开判断。
本篇默认讨论 x86 的二进制补码;C 示例按课件的 32 位 int、32 位 unsigned int 环境理解。涉及语言规则时,使用 C11/C17 范围的规则;不要把机器指令的截断结果直接当成 C 有符号溢出的保证。
同一组位如何表示正负数
对 w 位无符号整数,范围是 0…2^w-1。对 w 位补码整数,范围是 -2^(w-1)…2^(w-1)-1。若某个位模式按无符号读作 U,则其补码真值为:最高位是 0 时等于 U,最高位是 1 时等于 U-2^w。
例如 85H 的无符号值为 133,按 8 位补码解释是 133-256=-123。反过来,-123 的编码为 256-123=133=85H。这比先转原码、再取反、再加一更便于检查答案。
| 8 位编码方式 | -5 的位模式 | 零的个数 | 表示范围 |
|---|---|---|---|
| 原码 | 10000101 | 两个 | -127…127 |
| 反码 | 11111010 | 两个 | -127…127 |
| 补码 | 11111011 | 一个 | -128…127 |
这里 w 是包括符号位在内的总位数。课件有时把 n 定义为数值部分位数,照抄公式前必须先对齐符号含义。补码最小负数没有同宽正数对应,因此 8 位 80H 的补码取负后低八位仍是 80H,但数学上的 +128 超出了可表示范围。
加法器只处理位 标志位帮助解释结果
在固定 w 位加法中,低 w 位结果相当于对 2^w 取模。同一电路可以服务有符号和无符号运算,区别体现在怎样解释结果以及观察哪些标志。
| 8 位运算 | 低八位 | CF | OF | 解释 |
|---|---|---|---|---|
| 7FH + 01H | 80H | 0 | 1 | 127+1 超出补码范围,未产生第九位进位 |
| FFH + 01H | 00H | 1 | 0 | 无符号255+1越界;有符号-1+1正常 |
| 80H - 01H | 7FH | 0 | 1 | 无符号128-1正常;有符号-128-1越界 |
| 00H - 01H | FFH | 1 | 0 | 无符号需要借位;有符号0-1正常 |
OF 在加法中可用“同号相加,结果异号”判定;减法中用“异号相减,结果符号与被减数不同”判定。x86 的减法 CF 表示无符号借位。ZF 看结果是否为零,SF 看结果最高位。
比较题常通过 CMP a,b 所作的 a-b 来判断。无符号小于看 CF=1,无符号大于看 CF=0 且 ZF=0;有符号小于看 SF≠OF,有符号大于看 ZF=0 且 SF=OF。只用 SF 判断有符号大小,会漏掉减法溢出的情况。
扩展 截断与混合类型比较
无符号扩展在高位补零;补码符号扩展在高位重复符号位。85H 零扩展为 0085H,表示 133;符号扩展为 FF85H,仍表示 -123。截断则保留低位,例如 1234H 截为八位是 34H,原真值未必保留。
C 表达式还要先做整数提升和通常算术转换。以 -1 < 1U 为例,两边分别是 int 和 unsigned int;在指定的同宽环境中,-1 转成无符号最大值,所以比较为假。相反,较宽有符号类型若能容纳另一无符号类型的全部值,可以转向该有符号类型。因此“只要出现 unsigned,整个表达式都无符号”不是完整规则。相关规则见 C11 草案 N1570 的 6.3.1.1 与 6.3.1.8。
再看一个常见循环:
/* 假设 a 至少有 len 个元素,且 sum 的累计不会发生有符号溢出 */
int sum = 0;
for (unsigned i = 0; i < len; ++i)
sum += a[i];
这比 i <= len-1 更直接。若 len 是 unsigned 且为 0,len-1 就会形成无符号最大值,使“空数组不进入循环”的预期落空。错误发生在下标真正越界之前的边界表达式中。
机器回绕不能代替 C 语言规则
无符号 C 运算按模规则进行;普通 C 有符号整数算术溢出是未定义行为。因此 int x=INT_MAX; x=x+1; 不能作为可移植的“输出 INT_MIN”例子,编译器也可以基于程序不发生这种溢出的前提进行优化。GCC 可用选项改变部分行为,但必须把选项写成条件。可参考 GNU 对有符号溢出的说明。
检测加法是否安全,宜在加法之前判断边界:
#include <limits.h>
int add_ok(int a, int b)
{
if (b > 0 && a > INT_MAX - b) return 0;
if (b < 0 && a < INT_MIN - b) return 0;
return 1;
}
这段函数只判断是否可加,并不计算可能溢出的和。使用时仍应让真正的加法发生在检查通过之后。
移位也需要注明层次:x86 的 SAR 复制符号位,负数结果向负无穷方向取整;C11/C17 中负数右移由实现定义,而带符号整数除法截断趋向零。故 -14/4=-3,按算术右移两位却得到 -4。题设已规定算术右移时,负数加上 2^k-1 再右移 k 位,可用于实现除以 2^k 的向零取整;这不能泛化为不附条件的标准 C 等式。移位边界见 N1570 的 6.5.7。
自测与简答
- 八位
FEH的无符号值与补码真值分别是多少?符号扩展到十六位是什么? - 八位
80H+80H的低位结果、CF、OF 分别是多少? - 在本篇32位环境下,
-2 < 1U是真是假?
简答: ① 254、-2、FFFEH。② 00H、1、1,两种解释都超出了各自范围。③ 假;-2 转为 4294967294U。答题顺序应当是先写类型,再写转换,再比较。
来源定位
主要依据 2.数据的机器级表示与处理-V1.0.pptx 第21—57页的整数编码与类型、第129—152页的转换和标志、第157—168页的乘除与移位。课件中的部分程序用于展示特定机器结果;本文对有符号溢出、混合类型、负数右移补充了语言版本与实现前提。
讨论这篇文章
关于《ICS 与 8086 复习 03:补码 溢出与类型转换》的补充、不同意见或延伸话题,都可以留在这里。
滚动到这里时自动载入留言。