[comp.lang.c] Detecting overflow

ken@aiai.ed.ac.uk (Ken Johnson) (05/18/89)

Since I posted a C program which detects overflow in int multiplication
at immense cost in time terms, I received the following solution
by e-mail:

	if ( a != 0 && (a * b)/b == a) { /* No overflow */ }

My first thought is the first test should really be applied to b,
not a, to prevent overflow, giving

	if ( b != 0 && (a * b)/b == a) { /* No overflow */ }

However, this is not always going to work. Consider the case of
a = 512 (0x200), b = 64 (0x40). Then in 16-bit arithmetic,

	a != 0 is true
	b != 0 is true
	(a * b) = 0x8000 (overflowing) but 0x8000/0x40 = 0x200

...so I don't think this is guaranteed to work either.

A further example would be 521 * 63 (0x209 * 0x3f).

-- 
Ken Johnson, AI Applications Institute, 80 South Bridge, Edinburgh EH1 1HN
E-mail ken@aiai.ed.ac.uk, phone 031-225 4464 extension 212
Half of what I have said in this article is rubbish. I don't know which half.

mcdaniel@uicsrd.csrd.uiuc.edu (Tim McDaniel) (05/20/89)

[My earlier article with the same title, <1067@garcon.cso.uiuc.edu>,
was incomplete.  I intend to cancel it.  Sorry if you see both of
these.]

In article <455@skye.ed.ac.uk> ken@aiai.UUCP (Ken Johnson) writes:
>Since I posted a C program which detects overflow in int multiplication
>at immense cost in time terms, I received the following solution
>by e-mail:
>
>	if ( b != 0 && (a * b)/b == a) { /* No overflow */ }

As I noted eariler, it doesn't work if overflow on multiply causes a
fatal trap.  (However, if it silently truncates, it's almost correct.
I should have made this a special case in MY immense-cost code.)  By
the way, Ken's code wasn't so bad.  It did have a loop, but it avoided
division, which can be slooooow.

>However, this is not always going to work. Consider the case of
>a = 512 (0x200), b = 64 (0x40). Then in 16-bit arithmetic,

... presumably, 2s-complement, and "*" silently left-truncates on
overflow ...

>	(a * b) = 0x8000 (overflowing)

Yes.  0x8000 == -32768 (where "overflow" means "the result is an
integer, but cannot be represented exactly in the storage provided")

> but 0x8000/0x40 = 0x200

No.  The ANSI C standard requires that -32768/64 == -512.  The
division here (0x8000/0x40 == 0x200) is correct for unsigned, but if
it was unsigned arithmetic, there was no overflow; in unsigned, 0x8000
represents 32768, which is the correct result.  So the test worked in
this case.

>A further example would be 521 * 63 (0x209 * 0x3f).
Same type of result.

>...so I don't think this is guaranteed to work either.

A problem case is actually a = 0x8000 = -32768, b = -1.
        a * b = 0x8000 = -32768
This has overflowed, since the correct answer, 32768, can't be stored
in 16 bits 2s-complement. Then
        -32768 / b = -32768 / -1 = -32768
for the same basic reason.

Here's another, less probable case: suppose that an overflow on "*"
generates INT_MAX (or INT_MIN, depending on the proper sign).  Then
        -1311 * 25 = -32775
This would be reduced to INT_MIN = -32768 (say).
        -32768 / 25 = -1310.72000 = uhhh ....

An ANSI C implementation is permitted to generate either -1310
(truncate) or -1311 (round to -infinity).  If it chooses the latter,
the test fails.

--
"6:20 O Timothy, keep that which is committed to thy trust, avoiding
profane and vain babblings, and oppositions of science falsely so
called: 6:21 Which some professing have erred concerning the faith."

Tim, the Bizarre and Oddly-Dressed Enchanter  |  mcdaniel@uicsrd.csrd.uiuc.edu
            {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel
            mcdaniel%uicsrd@{uxc.cso.uiuc.edu,uiuc.csnet}

tarvaine@tukki.jyu.fi (Tapani Tarvainen) (05/20/89)

In article <455@skye.ed.ac.uk> ken@aiai.UUCP (Ken Johnson) writes:

[about detecting overflow in integer multiplication]

>	if ( b != 0 && (a * b)/b == a) { /* No overflow */ }
>
>However, this is not always going to work. Consider the case of
>a = 512 (0x200), b = 64 (0x40). Then in 16-bit arithmetic,
>
>	a != 0 is true
>	b != 0 is true
>	(a * b) = 0x8000 (overflowing) but 0x8000/0x40 = 0x200
>
>...so I don't think this is guaranteed to work either.

If you're doing unsigned multiplication the above isn't overflow,
if signed then division is presumably signed also and
0x08000/0x040 = -32768/-64 = -512 = 0x0FE00 != 0x0200.

Actually I'm not at all sure signed multiplication gives
the above result in all processors, or indeed that any
particular result can be relied on.  Nonetheless, the test
works even if we assume that a random number is generated
whenever overflow occurs:  If c/b == a && c%b == 0 then c == a*b, 
without overflow, as long as division can be relied on.
Moreover, if a*b overflows, then c/b == a && c%b != 0
isn't possible.

Division can overflow in 2's complement arithmetic 
in one case only, namely when the most negative
number is divided by -1 (e.g., -32768/-1 with 16 bits)
(in one's complement arithmetic it NEVER overflows)
so if -32768*-1 gives -32768 and -32768/-1 also gives -32768
then the test will fail.  The truly paranoid can can test
for this possibility separately, e.g., a<0 && -a<0
doesn't take much time.

Of course there's still the possibility that a too smart compiler
will optimize (a*b)/b as a ... but if you write it as

c = a * b;
if (c / b != a) { /* overflow */ ...

you should be safe; 

if ((c = a * b) / b != a) { ...

is probably OK as well (anybody know what pANS says about expression
optimization in cases like this?).

Finally:  All this assumes multiplication can only result in normal
numbers - if there is an architecture with integer NaNs or something
which can result from multiplication overflow but isn't guaranteed
to work reasonably in division, then all bets are off.  (E.g.,
if integers are stored as 64 bits but behave as 48-bit ones,
except when overflow occurs ... I vaguely remember having heard
of such a system.)
-- 
Tapani Tarvainen                 BitNet:    tarvainen@finjyu
Internet:  tarvainen@jylk.jyu.fi  -- OR --  tarvaine@tukki.jyu.fi

mcdaniel@uicsrd.csrd.uiuc.edu (Tim McDaniel) (05/21/89)

In article <455@skye.ed.ac.uk> ken@aiai.UUCP (Ken Johnson) writes:
: [about detecting overflow in integer multiplication]
:	if ( b != 0 && (a * b)/b == a) { /* No overflow */ }

In article <723@tukki.jyu.fi> tarvaine@tukki.jyu.fi (Tapani Tarvainen) writes:
>Nonetheless, the test works even if we assume that a random number is
>generated whenever overflow occurs ... as long as division can be
>relied on.

I answered this in another posting.  Basically, "division can't be
relied on" for negative divisors.

>Division can overflow in 2's complement arithmetic in one case only,
>namely when the most negative number is divided by -1 (e.g.,
>-32768/-1 with 16 bits) ... The truly paranoid can can test
>for this possibility separately, e.g., a<0 && -a<0
>doesn't take much time.

In such a case, -a == -INT_MIN overflows, which could trap.  In my
code, I checked for all cases in which -1 is an operand.  The check is
much easier to do and generating the result is much faster.

>Of course there's still the possibility that a too smart compiler
>will optimize (a*b)/b as a ...

One of the most heatedly-debated issues in the ANSI C standardization
effort was whether it should be legal to optimize past parenthesis
boundaries.  Existing practice was that it was legal, and it was
proposed that the unary "+" operator would disable optimization.  In
this case, the expression above would have had to write "+(a*b)/b" to
assure the desired behavior.  After much debate, it was decided to
follow the FORTRANish rule to make it illegal.

Thus, a pANS C-compliant compiler can't be "too smart" here.  Under
pANS C, "(a*b)/b" cannot be optimized to "a".  However, if it's not
pANS C-compliant, all bets are off.  (That's the reason for having a
standard in the first place, so that at least SOME bets can be made.
:-)

>integers are stored as 64 bits but behave as 48-bit ones, except when
>overflow occurs ... I vaguely remember having heard of such a
>system.)

Working from memory here: I think one such system was the CDC Cyber
174 and 175.  Floating-point numbers were 64 bits long, with a 15 bit
exponent field.  The "mantissa" field was not a fraction, as on most
systems today, but an integer.  These were ones-complement machines.
Therefore, the f.p. value was just
        mant_sign * (mantissa * 2 ** (exp_sign * exponent))

So integers were simply floating-point numbers with an exponent of 0.
I'm not sure whether there were any special integer instructions, or
whether floating-point instructions were used instead.  In the latter
case, overflow would have ... interesting ... effects.  Any loop
counter being incremented by 1 that overflowed (overflew? :-) would
hit INT_MAX+1 and stick there.

--
"6:20 O Timothy, keep that which is committed to thy trust, avoiding
profane and vain babblings, and oppositions of science falsely so
called: 6:21 Which some professing have erred concerning the faith."

Tim, the Bizarre and Oddly-Dressed Enchanter  |  mcdaniel@uicsrd.csrd.uiuc.edu
            {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel
            mcdaniel%uicsrd@{uxc.cso.uiuc.edu,uiuc.csnet}

scjones@sdrc.UUCP (Larry Jones) (05/21/89)

In article <1076@garcon.cso.uiuc.edu>, mcdaniel@uicsrd.csrd.uiuc.edu (Tim McDaniel) writes:
> Under pANS C, "(a*b)/b" cannot be optimized to "a".  However, if it's not
> pANS C-compliant, all bets are off.  (That's the reason for having a
> standard in the first place, so that at least SOME bets can be made.
> :-) [where a and b are both integers]

Sorry, but you're wrong -- pANS DOES allow the expression to be
optimized provided the optimized expression gives the same result
as the original expression in all cases where the result is well
defined.  If a and b are both integers, then the expression "a"
does give the same result as "(a*b)/b" except when "(a*b)/b"
overflows.  If it overflows, the results are undefined and
getting the "correct" answer without generating an overflow is
certainly an allowable response to undefined behavior.
----
Larry Jones                         UUCP: uunet!sdrc!scjones
SDRC                                      scjones@SDRC.UU.NET
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
"You can't get a body like mine in a bottle --
unless you push REAL HARD." - Judy Tenuta / Dr. Pepper

jfc@athena.mit.edu (John F Carr) (05/23/89)

In article <731@sdrc.UUCP> scjones@sdrc.UUCP (Larry Jones) writes:
>If a and b are both integers, then the expression "a"
>does give the same result as "(a*b)/b" except when "(a*b)/b"
>overflows.  If it overflows, the results are undefined and
>getting the "correct" answer without generating an overflow is
>certainly an allowable response to undefined behavior.

If they are unsigned integers, this optimization is not possible,
since overflow is ignored (i.e. given n bit words,

  (2 * (2^n - 1)) / 2 == 2^(n-1) - 1 

is required with unsigned math [becuase the intermediate value,
2*(2^n - 1) must be truncated to 2^n - 2], while the true answer is

  (2 * (2^n - 1)) / 2 ==  2^n - 1

).

    --John Carr (jfc@athena.mit.edu)

   John Carr             "When they turn the pages of history,
   jfc@Athena.mit.edu     When these days have passed long ago,
   bloom-beacon!          Will they read of us with sadness
   athena.mit.edu!jfc     For the seeds that we let grow?"  --Neil Peart