什么是扩大整数运算?
What are widening integer operations?
让我们以 Add
和 Multiply
为例。哪一个可以归类为加宽?
假设输入是 有符号字符(即长度为 8 位)的 2 的补码,除非另有声明。
正数加法
1+2 = 3
这个操作好像没有扩大。 1、2、3都在一个字符里。
但是,250 + 6
会溢出一个无符号字符。那么这是在扩大吗?
对于有符号类型也可以这样做,125 + 5
将有符号字符溢出到符号位中。这是在加宽吗?
负数加法
-2-3 = -5
这会使二进制字符溢出 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
溢出通常会被丢弃,但是,这是否被视为扩大操作?
正数乘法
1 * 2 = 2
即使结果仍然适合原始数据类型,所有的乘法运算都会扩大吗?
上面的例子,2
仍然适合 8 位字符,但是,如果我用二进制手工计算,额外的 0s
会附加到结果的左侧,结果会被丢弃.
负数乘法
-2 * -3 = 6
为简单起见,假设我们的输入是 4 位。这会按预期溢出其大小的两倍,即结果变为 8 位:
1110 * 1101 = 10110110
丢弃高 4 位,低 4 位等于 6
,符合预期。
这算不算扩大操作?
师
我没有详细介绍除法,但我认为它被认为是一个 narrowing 操作。
加宽与否也是一个属性做数学运算的汇编指令。但是,选择提供何种指令取决于数学运算的固有属性,例如该乘法可以产生更广泛的结果。
在数学上,两个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 = 16384
SCHAR_MIN 平方(即 0x80 * 0x80 = 0x4000
,请注意结果中的符号位为 0,但正下方的位被设置)。或者无符号 8 位 255 * 255 = 65025
即 0xff ^ 2 = 0xfe01
.
许多 CPU 确实提供了扩展的 形式 乘法,例如x86 的 mul r/m32
和 imul r/m32
将乘积写入 EDX:EAX.
x86 也有非扩展 imul r, r/m32
以防你不关心高半部分,例如为 unsigned * unsigned
实现 C 语义,产生 unsigned
结果,t运行 覆盖完整结果。 (请注意,无论将输入解释为有符号还是无符号,低半部分都是相同的,只有高半部分不同。这就是为什么 x86 不费心提供类似的 mul r, r/m32
指令)
一些 ISA 不提供扩展乘法,而是提供单独的低半指令和有符号高半指令/无符号高半指令。 (运行 它们背靠背可能会让一些实现在内部将它们融合成一个单一的扩大乘法,但更简单的实现可能只是 运行 它们分开,软件可能只使用一个或另一个。)
有些 ISA 甚至可能根本不提供获取高半部分的方法,例如ARM Cortex-M0 微控制器只有 32b x 32b => 32b 乘法,没有 umull
。将其用作更宽乘法(如 64x64 => 128 位)的构建块可能意味着将输入分解为 16 位块以应用通常的扩展精度技术,即 16b x 16b => 32b 乘法。
从数学上讲,整数除法最多可以产生被除数宽度的商。出于扩展精度的目的,支持更广泛的红利(参见 )很有用,但编译器通常不使用此功能。 (尤其是在像 x86 这样的 ISA 上,如果商不适合 32 位,则 64b / 32b => 32b 除法会出错。)
但是,是的,如果 ISA 的除法指令具有宽被除数,例如 x86 的隐式 EDX:EAX 用于 div
和 idiv
,这称为缩小除法。
加宽addition/multiplication表示运算结果大于至少一个输入。
特别是在 arm neon 中,加宽运算符不同于(en)加长指令,因此加宽 addition/accumulation 将 8 位源添加到 16 位目标,而长加法将两个 8 位源添加到 16 位目标。这同样适用于 16 位和 32 位源。
0001+0002==00000003 加宽。
同样 01*02==0002 加宽,02 不加宽。
在 Intel x86 ISA 中,乘法设计扩大,产生 32 位输出到寄存器对 dx:ax。同样,随着 IA 的发展,分歧正在缩小。
相反 c/c++ 语义 if int32/uint32 算术主要是模块化的,丢弃无限精度结果的高位。
让我们以 Add
和 Multiply
为例。哪一个可以归类为加宽?
假设输入是 有符号字符(即长度为 8 位)的 2 的补码,除非另有声明。
正数加法
1+2 = 3
这个操作好像没有扩大。 1、2、3都在一个字符里。
但是,250 + 6
会溢出一个无符号字符。那么这是在扩大吗?
对于有符号类型也可以这样做,125 + 5
将有符号字符溢出到符号位中。这是在加宽吗?
负数加法
-2-3 = -5
这会使二进制字符溢出 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
溢出通常会被丢弃,但是,这是否被视为扩大操作?
正数乘法
1 * 2 = 2
即使结果仍然适合原始数据类型,所有的乘法运算都会扩大吗?
上面的例子,2
仍然适合 8 位字符,但是,如果我用二进制手工计算,额外的 0s
会附加到结果的左侧,结果会被丢弃.
负数乘法
-2 * -3 = 6
为简单起见,假设我们的输入是 4 位。这会按预期溢出其大小的两倍,即结果变为 8 位:
1110 * 1101 = 10110110
丢弃高 4 位,低 4 位等于 6
,符合预期。
这算不算扩大操作?
师
我没有详细介绍除法,但我认为它被认为是一个 narrowing 操作。
加宽与否也是一个属性做数学运算的汇编指令。但是,选择提供何种指令取决于数学运算的固有属性,例如该乘法可以产生更广泛的结果。
在数学上,两个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 = 16384
SCHAR_MIN 平方(即 0x80 * 0x80 = 0x4000
,请注意结果中的符号位为 0,但正下方的位被设置)。或者无符号 8 位 255 * 255 = 65025
即 0xff ^ 2 = 0xfe01
.
许多 CPU 确实提供了扩展的 形式 乘法,例如x86 的 mul r/m32
和 imul r/m32
将乘积写入 EDX:EAX.
x86 也有非扩展 imul r, r/m32
以防你不关心高半部分,例如为 unsigned * unsigned
实现 C 语义,产生 unsigned
结果,t运行 覆盖完整结果。 (请注意,无论将输入解释为有符号还是无符号,低半部分都是相同的,只有高半部分不同。这就是为什么 x86 不费心提供类似的 mul r, r/m32
指令)
一些 ISA 不提供扩展乘法,而是提供单独的低半指令和有符号高半指令/无符号高半指令。 (运行 它们背靠背可能会让一些实现在内部将它们融合成一个单一的扩大乘法,但更简单的实现可能只是 运行 它们分开,软件可能只使用一个或另一个。)
有些 ISA 甚至可能根本不提供获取高半部分的方法,例如ARM Cortex-M0 微控制器只有 32b x 32b => 32b 乘法,没有 umull
。将其用作更宽乘法(如 64x64 => 128 位)的构建块可能意味着将输入分解为 16 位块以应用通常的扩展精度技术,即 16b x 16b => 32b 乘法。
从数学上讲,整数除法最多可以产生被除数宽度的商。出于扩展精度的目的,支持更广泛的红利(参见
但是,是的,如果 ISA 的除法指令具有宽被除数,例如 x86 的隐式 EDX:EAX 用于 div
和 idiv
,这称为缩小除法。
加宽addition/multiplication表示运算结果大于至少一个输入。
特别是在 arm neon 中,加宽运算符不同于(en)加长指令,因此加宽 addition/accumulation 将 8 位源添加到 16 位目标,而长加法将两个 8 位源添加到 16 位目标。这同样适用于 16 位和 32 位源。
0001+0002==00000003 加宽。
同样 01*02==0002 加宽,02 不加宽。
在 Intel x86 ISA 中,乘法设计扩大,产生 32 位输出到寄存器对 dx:ax。同样,随着 IA 的发展,分歧正在缩小。
相反 c/c++ 语义 if int32/uint32 算术主要是模块化的,丢弃无限精度结果的高位。