Hi,
01000101 wrote:I need some help understanding how to implement the multiplication and division functions. The addition and subtraction functions were pretty straight forward as far as carry usage goes, but these two are seemingly far more complex of beasts.
For multiplication, back in school you would've been taught how to multiply 2 large numbers. For example:
Code: Select all
1 2 3 4
* 1 2
-----------
2 4 6 8
+ 1 2 3 4 0
-----------
= 1 4 8 0 8
This is "base 10" mathmatics, where there's 10 digits. The thing is, it works the same in any base. What if you used "base 256" instead of "base 10"?
Code: Select all
0x01 0x02 0x03 (66051 in decimal)
* 0x01 0x02 (258 in decimal)
---------------------
0x02 0x04 0x06 (132102 in decimal)
+ 0x01 0x02 0x03 0x00 (16909056 decimal)
---------------------
= 0x01 0x04 0x07 0x06 (17041158 decimal)
See how it works the same?
You could multiply a pair of 256-bit integers together using this method without much trouble, but with a 32-bit CPU why use "base 256"? You could just as easily use "base 4294967296". With "base 4294967296" working on 256-bit integers, you'd actually be multiplying an 8-digit number with another 8-digit number, which works out to a maximum of 64 smaller (32-bit) multiplications, and the final 512-bit addition would be done with sixteen 32-bit additions (using the "ADC" instruction).
Note: when multiplying 256-bit integers together, the result will be a 512-bit integer (or a smaller integer that could overflow).
Also, back in school you would've been taught "long division". For example:
Code: Select all
1 2 3 4 <-answer
1 2 ) 1 4 8 0 9
- 1 2
----
2 8
- 2 4
----
4 0
- 3 6
----
4 9
- 4 8
----
0 1 <- remainder
This also works in any number base, including "base 4294967296".
Lastly, for shifting there's a pair of instructions "SHLD" and "SHRD" which shift N bits from the source operand into the destination operand. For example, if EAX = 0x01234567 and EBX = 0x89ABCDEF, then after doing "SHLD eax, ebx, 16" you'd end up with EAX = 0x456789AB. This saves you the hassle of shifting one bit at a time in a loop.
However, if the shift count is too large you'll need to "pre-shift". For example, if you're operating on a string of dwords and the shift count is larger than 32, then you'd need to use "rep movsd" to pre-shift it by "N / 32 bits" and then use "shift count = shift_count & 0x1F" with SHLD/SHRD.
Also note that it's possible to use addition and shifting to implement multiplication; and subtraction, comparison and shifting to implement division. This is actually the exact same thing as above, but using "base 2". AFAIK this is the method that CPU's use internally to implement multiplication and division.
For performance reasons you'd probably want to use the largest base you can. For e.g. it's better to multiply a pair of 8-digit "base 4294967296" numbers together than it is to multiply a pair of 32-digit "base 256" numbers together or a pair of 256-digit "base 2" numbers (even though you'd get identical results).
Cheers,
Brendan