Overflow and range checking for mul/div

This is a discussion on Overflow and range checking for mul/div within the C++ Programming forums, part of the General Programming Boards category; I'm currently trying to implement range checking and overflow/underflow checking for multiplication and division, but... it's more complicated than addition ...

Overflow and range checking for mul/div

I'm currently trying to implement range checking and overflow/underflow checking for multiplication and division, but... it's more complicated than addition and subtraction.

My current way is somewhat messy.
If we assume that a custom range is set to:
Min: 0
Max: 100 000

And we have the number 15 and want to multiply by 10 000, we would get 150 000, which is above > 100 000 and thus would result in overflow and a number that is 50 000 afterwards.
The problem is that checking if Result > upper bound is not always feasible, since for example, if the upper bound is the maximum range for a 64-bit integer. The result would be an overflow, so I can't do it that way. This suggests another way.
Using mathematics, I came up with:

Min: 0
Max: 100 000
Base: 15
Multiply By: 10 000

100 000 / 15 = 6666
(Divide upper range with the current number stored (m_Data). Basically see how many times we can multiply this number with until we hit the upper bound. Also truncate to integer.)

6666 >= 10 000
(Check if the number we can multiply with is larger or equal to the amount we want to multiply with. If the result is true, then we will have no overflow.)
If true, no overflow

(If no)
10 000 - 6666 = 3334
(Get the remaining amount to multiply with that would exceed the upper bound.)
15 * 6666 = 99 990
(Multiply the current number with the maximum number we can multiply by that does not exceed the upper bound.)
100 000 - 99 990 = 10
(Calculate the difference between the maximum number and the result of the current operation. This is to take into account the lost digits due to the integer truncation.)
15 * 3334 = 50 010
(Multiply the number by the amount that exceeded the upper bound.)
50 010 - 10 = 50 000
(Subtract the difference due to the integer truncation.)

Since floating point numbers aren't reliable, I chose to do integer math instead.
But this is a pretty long way of doing it and it doesn't even take into account negative numbers on either side!
There must a better way to do it (big precision library to handle bigger numbers?) or a way to modify the algorithm or whatever to call it to work with negative numbers.

This is only for multiplication, as well. Then I'd have to implement a similar method for division. So I'm looking for insight into the matter.
Anything that might help would be nice.

I'm not too familiar with assembly, but isn't there an assembly instruction (at least on x86) that can tell you if there was an overflow in the previous operation?
If so, that would help with the case of integer overflow.

I'm not too familiar with assembly, but isn't there an assembly instruction (at least on x86) that can tell you if there was an overflow in the previous operation?
If so, that would help with the case of integer overflow.

Sure, but that would require writing the math in assembler too, because the overflow flag is transient - it doesn't remain set if you do any other math operation (e.g. the compiler does some math to calculate the address of, say, a member variable). So you need to pair the calculation itself with a jump on overflow (or interrupt on overflow, but that's a bit excessive, I think).

And of course, that only applies if you overflow the actual hardware value. So if the calculation is artificially bounded (e.g 100000), then overflow flag in the processor won't work.

Also, the entire operation looks like an attempt to reproduce '&#37;' without using '%'.

My bad. I forgot all about it.

Originally Posted by cpjust

I'm not too familiar with assembly, but isn't there an assembly instruction (at least on x86) that can tell you if there was an overflow in the previous operation?
If so, that would help with the case of integer overflow.

Yes, as mats explained, it doesn't seem very feasible.

Originally Posted by Sander

- Will your "custom ranges" always be within the range of an available integer type?
- Do you want to stick to integers?

Currently I'm sticking with integers, native built-in types.
Although, however, since the class is templated, it is actually possible to supply a custom class as a wrapper for custom integers later.

I don't think it will be easy to work out the specifications for your new type. I assume you're trying to make some kind of "wrapper" allowing you to do ordinary math operations, but keep the result within a (custom) range and set an overflow flag upon wrapping around.

But suppose you set a custom range of 10 .. 20 and add 7 to 15. The result would be 22, which is over the specified maximum so you set the overflow flag, but then what do you set the result to? You can't set it to 2 because that would also be out of the range, and yet no other choice makes sense...

I don't think it will be easy to work out the specifications for your new type. I assume you're trying to make some kind of "wrapper" allowing you to do ordinary math operations, but keep the result within a (custom) range and set an overflow flag upon wrapping around.

But suppose you set a custom range of 10 .. 20 and add 7 to 15. The result would be 22, which is over the specified maximum so you set the overflow flag, but then what do you set the result to? You can't set it to 2 because that would also be out of the range, and yet no other choice makes sense...

Wouldn't it wrap around to 12 in that case?
Anyways, if it overflowed, what difference does it make what you set the value to? The result is out of range.
What about throwing an exception on underflow/overflow?

Quotients don't overflow. If the quotient would wrap around, then a and b should have wrapped around, meaning that you are dividing numerals in a type too small. I suppose you could downcast the result to a smaller type too, but that would be a construction exception, since you can't fit the number into the range without truncating bits.

The basic principles you need to remember come down to the causes of overflow and underflow (assuming an underflow is producing a result less than allowed minimum, and overflow is producing a value that wraps or exceeds allowed maximum).

Assume we have two positive (non-zero) values a and b that are constrained to in the range [minimum, maximum]. Overflow on multiplication will only occur if a*b exceeds the maximum allowed value. i.e. a*b > maximum. That can only occur if both values are greater than 1 (multiplying a value in range by a value < 1 cannot give overflow). So a test for overflow is "if (a > 1 && b > 1 && a *b > maximum)" which can be recast as "if (a > 1 && b > 1 && a > maximum/b)" [this can be simplified, obviously, if minimum exceeds 1].

It is possible to formulate similar tests for underflow on multiplication or overflow/underflow on division. I'll leave those as an exercise.

The key is remembering basic mathematical relationships between inequality relationships. If a, b and c are all positive, then a test "if (a*b > c)" is the same as "if (a > c/b)".

Integer division cannot produce overflow. Divide by zero is a special case you already have to handle.

For basic integral types in C/C++ that is true. It is not true for all integral types (eg other languages allow specification of integral types with values in specified ranges, and such things can be implemented as a class in C++).

If your acceptable range is [-10,-1] then overflow on division is possible. -10/-1 equals 10 which exceeds any value in the accepted range.