In Win32, there is an API call called “MulDiv”:
The MulDiv function multiplies two 32-bit values and then divides the 64-bit result by a third 32-bit value. The return value is rounded up or down to the nearest integer.
int WINAPI MulDiv(
int nNumber,
int nNumerator,
int nDenominator
);
If a divide overflow (including divide by zero) occurs, MulDiv returns -1. (Stupidly, there’s no way to actually check whether the result truly is -1 instead of an error.)
How do we implement this in x86-32? The official version does not use structured exception handling to simply catch the exception and handle it. MulDiv actually checks for overflow beforehand and never causes an exception.
The official implementation is several pages long, and I think we could do much better.
If this were the unsigned case, the entire function would be simple:
mov eax, [esp+4]
mul dword ptr [esp+8]
cmp edx, [esp+12]
jae overflow
div dword ptr [esp+12]
ret 12
overflow:
or eax, -1
ret 12
One thing I find almost funny, is that MS speaks of 32*32->64. These are signed values, so the actual data bit-count is 31*31->62. Yes, obviously we’ve got the sign to account for, but the actual data bits affecting the outcome are 31 and 62.
I also think this displays another serious shortsightedness by MS, to include *only* a signed version, when I can bet KJK’s furry tail the majority of uses of it is for (conceptually) unsigned values. Actually, I’m willing to be the majority of uses are from MS own GDI samples relating to font sizes or other unit conversions, and all doing anything else math related are indeed using the more obvious (a*__int64(b))/c). Forcing users to call a function with several pages worth of instructions, including a gazillion conditional jumps, when a version as simple as you displayed would have been enough for an uMulDiv…
Oh well, at least it’s not using short’s (imagine the partial register stalls on todays CPU’s ;-> ).
tamlin: Shorts are easy, you just zero or sign extend as necessary. Then you only need to check for divide by zero before calling “div” because divide overflow is impossible. You’d then determine whether there was overflow simply by the result.
“Actually, Iâm willing to be the majority of uses are from MS own GDI samples relating to font sizes or other unit conversions, and all doing anything else math related are indeed using the more obvious (a*__int64(b))/c).”
Don’t forget that older versions of the 32-bit Microsoft C compiler do not have __int64 at all, and so is older compilers from most other vendors.