技术

ICS 与 8086 复习 03:补码 溢出与类型转换

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。

自测与简答

  1. 八位 FEH 的无符号值与补码真值分别是多少?符号扩展到十六位是什么?
  2. 八位 80H+80H 的低位结果、CF、OF 分别是多少?
  3. 在本篇32位环境下,-2 < 1U 是真是假?

简答: ① 254、-2、FFFEH。② 00H、1、1,两种解释都超出了各自范围。③ 假;-2 转为 4294967294U。答题顺序应当是先写类型,再写转换,再比较。

来源定位

主要依据 2.数据的机器级表示与处理-V1.0.pptx 第21—57页的整数编码与类型、第129—152页的转换和标志、第157—168页的乘除与移位。课件中的部分程序用于展示特定机器结果;本文对有符号溢出、混合类型、负数右移补充了语言版本与实现前提。

系列导航

CONTINUE / PATH

从这里继续

顺着当前内容继续,或者换到另一个板块。

讨论这篇文章

关于《ICS 与 8086 复习 03:补码 溢出与类型转换》的补充、不同意见或延伸话题,都可以留在这里。

滚动到这里时自动载入留言。