[comp.lang.c] ceil

mcdaniel@astroatc.UUCP (Tim McDaniel) (09/08/89)

Recently I thought about how to compute
        ceil(x/y)
portably, where y != 0 and x and y are integers.  "ceil(x/y)" means
"the least integer >= x/y", where "/" means real (not integer)
division here.

I was lead to this topic by seeing (essentially)
        int howmany(int x, int y) { return (x+y-1)/y; }
        int roundup(int x, int y) { return (x+y-1)/y*y; }
howmany(x,y) is supposed to compute ceil(x/y), but it can overflow for
large x or y, even if x or y were unsigned.  Furthermore, it doesn't
work all the time.  If x=5 and y=-4,
        ceil(5/-4) is ceil(-1.25) is supposed to be -1
but     (5+(-4)-1)/-4 == 0/-4 == 0

I then considered
        int howmany(int x, int y) { return (x-1)/y+1;   }
but it can underflow, and still doesn't work for x=5 and y=-4.

A further complication is that pANS C does not define integer division
precisely.  All that is required is that
        (a/b)*b + a%b == a
and     absolute value(a%b) < b.
For instance, either of two cases can arise, as long as the machine is
consistent:
        5/-4 == -1 and 5%-4 == 1        (more common)
or      5/-4 == -2 and 5%-4 == -3       (less common)

I went back to a version of the original definition for positive x, y:
        ceil(x/y) is x div y         if x mod y == 0
                  is x div y + 1     otherwise
I considered x=5 or -5, and y=4 or -4, and derived the function
        int howmany(int x, int y)
                {
                if (y >= 0)
                        return x/y + (x%y > 0);
                else
                        return x/y + (x%y < 0);
                }
        int roundup(int x, int y) { return howmany(x,y)*y; }

If y is an integral power of 2, all the "/"s and "%"s can be replaced
throughout by appropriate shifts and masks, even if x can be negative.

There can still be overflow.  For instance, on a 2's complement
machine, INT_MIN < -INT_MAX, so if x=INT_MIN, y=-1, the new "howmany"
overflows.  However, it's impossible to represent "ceil(x/y)" in that
case (unles "long" or "long long" helps), so "howmany" can hardly get
the correct result.

Some might complain that the new "howmany" is much slower than the
old.  However, on a heavily pipelined machine like the Astronautics
ZS-1 (plug plug), the "/" and the "%" almost completely overlap,
taking about as much time as the "/" in the original "howmany".
Furthermore, the original didn't always work.  If you remove the
requirement that it work, I'll use
        int howmany(int x, int y) { exit(0); }
which causes great program execution time.
-- 

\    Tim McDaniel       "Tim, the Bizarre and Oddly-Dressed Enchanter"
 \   Internet: mcdaniel%astroatc.uucp@cs.wisc.edu
/ \  USENET:   ...!ucbvax!uwvax!astroatc!mcdaniel