问题
I have found out that both mul
and imul
can be used to multiply a signed number to an unsigned number.
For example:
global _start
section .data
byteVariable DB -5
section .text
_start:
mov al, 2
imul BYTE [byteVariable]
You can replace imul
with mul
, and the result would still be the same (-10
).
Are mul
and imul
exactly the same when multiplying a signed number to an unsigned number, or is there a difference between them?
回答1:
The upper half is different, as mentioned in the comments. If you don't care about the upper half, you can use either mul
or imul
, in all of their forms (the one-operand forms produce the upper half, but in this scenario you would ignore it).
If you do care about the upper half, neither mul
nor imul
works by itself, since they just multiply unsigned*unsigned and signed*signed, but you can fix it fairly easily.
Consider that a signed byte has the bit-weights -128, 64, 32, 16, 8, 4, 2, 1 while an unsigned byte has the bit-weights +128, 64, 32, 16, 8, 4, 2, 1. So you can represent the unsigned value of x
in signed format (I know this is confusing but it's the best I can do) as x + 256 x_7
(where x_7
is bit 7 of x
). The easiest way to see is probably to split it: x + 2 * 128 * x_7
. What's happening here is compensating for the -128 weight, first removing it by adding the value of bit 7 128 times and then going all the way up to the +128 weight by doing it again, of course this can be done in one step.
Anyway, multiplying that by some signed number y
and working it out gives 256 x_7 y + xy
, where xy
is the (double-width) result of imul
and 256 x_7 y
means "add y
to the upper half if the sign of x
is set", so a possible implementation is (not tested)
; al has some unsigned value
mov dl, al
sar dl, 7
and dl, [signedByte]
imul BYTE [signedByte]
add ah, dl
Naturally you could sign-extend one operand, zero-extend the other, and use a 16 bit multiplication (any, since the upper half is not relevant this way).
回答2:
Another behavior of flags. For MUL: OF=CF=1 when carry bit changes upper half; For IMUL: OF=CF=1 when carry bit changes sign bit in low part (or just sign bit in result for 2 or 3 operands form)
回答3:
x86 does have an instruction that multiplies signed bytes by unsigned bytes: SSSE3 pmaddubsw.
You can think of it as sign-extending one operand to 16 bits, zero-extending the other to 16 bits, then doing an NxN -> N-bit multiply. (For each SIMD element).
It also horizontally adds pairs of word products from adjacent bytes, but if you unpack the inputs with zeros (punpcklbw
or pmovzxbw
) then you can get each product separately.
Of course if you have SSE4.1 then you could just pmovsxbw
one input and pmovzxbw
the other input to feed a regular 16-bit pmullw
, if you don't want pairs added.
But if you just want one scalar result, movsx
/ movzx
to feed a regular non-widening imul reg, reg
is your best bet.
As Harold points out, mul r/m
and imul r/m
widening multiplies treat both their inputs the same way so neither can work (unless the signed input is known to be non-negative, or the unsigned input is known to not have its high bit set, so you can treat them both the same after all.)
mul and imul also set FLAGS differently: CF=OF= whether or not the full result fits in the low half. (i.e. the full result is the zero-extension or sign-extension of the low half). For imul reg,r/m
or imul reg, r/m, imm
, the "low half" is the destination reg; the high half isn't written anywhere.
来源:https://stackoverflow.com/questions/45495711/should-i-use-mul-or-imul-when-multiplying-a-signed-number-to-an-unsigned-num