Dan*_*Dan 1 assembly cpu-architecture twos-complement integer-arithmetic
让我们检查Add并Multiply作为例子。哪一种可以归类为加宽?
假设输入是2 的补码中的有符号字符(即长度为 8 位),除非另有声明。
1+2 = 3
Run Code Online (Sandbox Code Playgroud)
这个操作似乎没有扩大。1、2 和 3 都适合一个字符。
但是,250 + 6溢出无符号字符。那么这是在扩大吗?
同样可以用有符号类型完成,将有符号125 + 5字符溢出到符号位中。这是在扩大吗?
-2-3 = -5
Run Code Online (Sandbox Code Playgroud)
这会使二进制字符溢出 1 位:
1 1 1 1 1 1 1 0
+ 1 1 1 1 1 1 0 1
------------------
1 1 1 1 1 1 0 1 1
Run Code Online (Sandbox Code Playgroud)
溢出通常会被丢弃,但是,这是否被视为扩大操作?
1 * 2 = 2
Run Code Online (Sandbox Code Playgroud)
即使结果仍然适合原始数据类型,所有乘法是否都在扩大?
上面的例子2仍然适合一个 8 位字符,但是,如果我用二进制手工进行数学运算,额外的0s会附加到结果的左侧,结果被丢弃。
-2 * -3 = 6
Run Code Online (Sandbox Code Playgroud)
为简单起见,我们假设我们的输入是 4 位。这会按照预期的两倍大小溢出,即结果变为 8 位:
1110 * 1101 = 10110110
Run Code Online (Sandbox Code Playgroud)
高 4 位被丢弃,低 4 位6与预期相同。
这被认为是扩大操作吗?
我没有详细介绍除法,但我认为它被认为是一种缩小操作。
加宽与否也是执行数学运算的汇编指令的属性。然而,选择提供什么指令是由数学运算的固有属性决定的,例如乘法可以产生更广泛的结果。
在数学上,两个 n 位数字的和或差最多需要 n+1 位才能准确表示。例如无符号 3 位或二进制补码 4 位 7+7 = 14,即0b1110无符号或0b01110二进制补码。
大多数的CPU只提供一个非扩大实施的add/ sub,与额外位进入进位标志,如果有一个。(像 MIPS 这样的一些 ISA 根本没有 FLAGS,所以如果你想要结转,你必须从carry = (a+b) < a(无符号比较)中恢复它)。
在数学上,两个 n 位数字的乘积最多需要 2n 位才能精确表示。例如,有符号的 8 位-128 * -128 = 16384SCHAR_MIN 平方(即0x80 * 0x80 = 0x4000,注意结果中的符号位为 0,但正下方的位被设置)。或无符号 8 位255 * 255 = 65025即0xff ^ 2 = 0xfe01.
许多CPU都提供了一个扩大的形式倍增,例如86的mul r/m32和imul r/m32该写该产品EDX:EAX。
x86 还具有非扩展功能imul r, r/m32,以防您不关心上半部分,例如实现unsigned * unsigned生成unsigned结果的C 语义,截断完整结果。(请注意,无论将输入解释为有符号还是无符号,低半部分都是相同的,只有高半部分不同。这就是 x86 不费心提供类似mul r, r/m32指令的原因)
一些 ISA 不提供一个加宽乘法,而是提供单独的低半和有符号高半/无符号高半指令。(背靠背运行它们可能会让一些实现在内部将它们融合成一个单一的扩大乘法,但更简单的实现可以单独运行它们,软件可能只使用一个或另一个。)
一些 ISA 甚至可能根本没有提供获得高一半的方法,例如 ARM Cortex-M0 微控制器只有 32b x 32b => 32b 乘法,没有umull。将其用作更宽乘法(如 64x64 => 128 位)的构建块可能意味着将输入分解为 16 位块,以应用通常的扩展精度技术,即 16b x 16b => 32b 乘法。
在数学上,整数除法可以产生最多为被除数宽度的商。出于扩展精度的目的,支持更宽的除数很有用(请参阅为什么在使用 DIV 指令之前 EDX 应该为 0?),但编译器通常不使用此功能。(特别是在像 x86 这样的 ISA 上,如果商不适合 32 位,则 64b / 32b => 32b 除法会出错。)
但是,是的,如果 ISA 有一个除法指令有一个广泛的红利,就像 x86 的隐式 EDX:EAX for divand idiv,那被称为缩小除法。