Assembly 我是否应该使用“;mul";或;“伊穆尔”;将有符号数乘以无符号数时?

Assembly 我是否应该使用“;mul";或;“伊穆尔”;将有符号数乘以无符号数时?,assembly,x86,multiplication,unsigned,signed,Assembly,X86,Multiplication,Unsigned,Signed,我发现mul和imul都可以用来将有符号数乘以无符号数 例如: global _start section .data byteVariable DB -5 section .text _start: mov al, 2 imul BYTE [byteVariable] 您可以将imul替换为mul,结果仍然相同(-10) mul和imul在将有符号的数字乘以无符号的数字时是否完全相同,或者它们之间是否存在差异?如注释中所述,上半部分是不同的。如果不关心上半部分,可以在所有形式中

我发现
mul
imul
都可以用来将有符号数乘以无符号数

例如:

global _start

section .data
    byteVariable DB -5

section .text
_start:

mov al, 2
imul BYTE [byteVariable]
您可以将
imul
替换为
mul
,结果仍然相同(
-10


mul
imul
在将有符号的数字乘以无符号的数字时是否完全相同,或者它们之间是否存在差异?

如注释中所述,上半部分是不同的。如果不关心上半部分,可以在所有形式中使用
mul
imul
(单操作数形式产生上半部分,但在这种情况下,您将忽略它)

如果您确实关心上半部分,则
mul
imul
都不能单独工作,因为它们只是将unsigned*unsigned和signed*signed相乘,但您可以相当轻松地修复它

假设有符号字节具有位权重-128、64、32、16、8、4、2、1,而无符号字节具有位权重+​128, 64, 32, 16, 8, 4, 2, 1. 因此,您可以将
x
的无符号值表示为有符号格式(我知道这很混乱,但这是我能做的最好的方法),表示为
x+256 x_7
(其中
x_7
x
的第7位)。最简单的查看方法可能是拆分它:
x+2*128*x7
。这里发生的事情是补偿-128权重,首先通过将位7的值加128次来删除它,然后通过再次执行来一直增加到+128权重,当然这可以在一个步骤中完成

无论如何,将其乘以某个有符号数字
y
并计算得出
256 x_7 y+xy
,其中
xy
imul
的(双倍宽度)结果,
256 x_7 y
意味着“如果
x
的符号被设置,则将
y
添加到上半部分”,因此可能的实现(未经测试)


当然,您可以对一个操作数进行符号扩展,对另一个操作数进行零扩展,并使用16位乘法(任意,因为上半部分与此无关)。

标志的另一种行为。对于MUL:当进位改变上半部分时,OF=CF=1;对于IMUL:OF=CF=1,当进位更改低位的符号位(或仅对2或3个操作数形式的结果中的符号位)时,x86确实有一条将有符号字节乘以无符号字节的指令:

您可以将其视为符号将一个操作数扩展到16位,零将另一个操作数扩展到16位,然后执行NxN->N位乘法。(对于每个SIMD元件)

它还水平地添加相邻字节中的成对字乘积,但如果用零(
punpcklbw
pmovzxbw
)解包输入,则可以分别获得每个乘积

当然,如果您有SSE4.1,那么如果您不想添加对,您可以只需
pmovsxbw
一个输入和
pmovzxbw
另一个输入,就可以为常规的16位
pmullw


但是如果您只想要一个标量结果,那么最好的选择是
movsx
/
movzx
为常规非加宽
imul reg,reg

正如Harold指出的,
mul r/m
imul r/m
加宽乘法以相同的方式处理它们的两个输入,因此它们都不能工作(除非已知有符号输入为非负输入,或者已知无符号输入没有其高位集,因此可以对它们进行相同的处理。)


mul和imul也设置了不同的标志:CF=OF=完整结果是否适合下半部分。(即,完整结果是下半部分的零扩展或符号扩展)。对于
imul-reg,r/m
imul-reg,r/m,imm
,“下半部分”是目的地reg;高半部没有写在任何地方。

用不同的值尝试一下,看看你得到了什么。不,结果不一样,可能你只是检查了结果的低半部,结果是一样的。无符号的
mul
应该产生
502
,结果是
ax
@Jester你是对的,
ax
在使用
mul
时是
0x01F6
502
),在使用
imul
时是
0xFFF6
-10
)。始终使用
imul reg,r/m32
imul reg,r/m32,imm
如果您不需要高半部结果;它在现代CPU(1UOP)上更高效,因为它不必在任何地方写入高半音。这不是一个很清楚的描述。英特尔的手册对此做了更好的解释:如果上半部分为非零,则设置CF和OF。如果上半部分不是下半部分的符号扩展,则设置CF和OF。否则他们就被清除了。因此,在这两种情况下,CF=OF=结果是否符合下半部分。(imul的2和3操作数形式仅产生整数乘法的下半部分,但标志设置仍与1操作数形式相同。)
; al has some unsigned value
mov dl, al
sar dl, 7
and dl, [signedByte]
imul BYTE [signedByte]
add ah, dl