Should I use "mul" or "imul" when multiplying a signed number to an unsigned number?
Asked Answered
E

3

7

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?

Everard answered 3/8, 2017 at 22:46 Comment(4)
No, the result is not the same, presumably you just checked the low half of the result only and that is the same. The unsigned mul should produce 502 as result in ax.Servitor
@Servitor You are right, ax is 0x01F6 (502) when using mul, and 0xFFF6 (-10) when using imul.Everard
Always use imul reg, r/m32 or imul reg, r/m32, imm if you don't need the high-half result; it's more efficient on modern CPUs (1 uop) because it doesn't have to write the high half anywhere. agner.org/optimizeBeet
Related: Why is imul used for multiplying unsigned numbers?Galling
G
4

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).

Gora answered 3/8, 2017 at 23:52 Comment(0)
B
1

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.

Beet answered 19/1, 2020 at 23:58 Comment(0)
A
-1

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)

Anandrous answered 18/1, 2020 at 1:28 Comment(1)
That's not a very clear description. Intel's manual explains it better: mul sets CF and OF if the upper half is non-zero. imul sets CF and OF if the upper half isn't the sign-extension of the low half. Otherwise they're cleared. So in both cases CF=OF = whether or not the result fits in the low half. (The 2 and 3 operand forms of imul only produce the low half of the full multiply, but flags are still set the same as the one-operand form.)Beet

© 2022 - 2024 — McMap. All rights reserved.