Let a, b and c be non-large positive integers. Does a/b/c always equal a/(b * c) with C# integer arithmetic? For me, in C# it looks like:
int a = 5126, b = 76,
I liked this question so much I made it the subject of my blog on June 4th, 2013. Thanks for the great question!
Large cases are easy to come by. For example:
a = 1073741823;
b = 134217727;
c = 134217727;
because b * c
overflows to a negative number.
I would add to that the fact that in checked arithmetic, the difference between a / (b * c)
and (a / b) / c
can be the difference between a program that works and a program that crashes. If the product of b
and c
overflows the bounds of an integer then the former will crash in a checked context.
For small positive integers, say, small enough to fit into a short, the identity should be maintained.
Timothy Shields just posted a proof; I present here an alternative proof. Assume all the numbers here are non-negative integers and none of the operations overflow.
Integer division of x / y
finds the value q
such that q * y + r == x
, where 0 <= r < y
.
So the division a / (b * c)
finds the value q1
such that
q1 * b * c + r1 == a
where 0 <= r1 < b * c
the division ( a / b ) / c
first finds the value qt
such that
qt * b + r3 == a
and then finds the value q2
such that
q2 * c + r2 == qt
So substitute that in for qt
and we get:
q2 * b * c + b * r2 + r3 == a
where 0 <= r2 < c
and 0 <= r3 < b
.
Two things equal to the same are equal to each other, so we have
q1 * b * c + r1 == q2 * b * c + b * r2 + r3
Suppose q1 == q2 + x
for some integer x
. Substitute that in and solve for x
:
q2 * b * c + x * b * c + r1 = q2 * b * c + b * r2 + r3
x = (b * r2 + r3 - r1) / (b * c)
where
0 <= r1 < b * c
0 <= r2 < c
0 <= r3 < b
Can x
be greater than zero? No. We have the inequalities:
b * r2 + r3 - r1 <= b * r2 + r3 <= b * (c - 1) + r3 < b * (c - 1) + b == b * c
So the numerator of that fraction is always smaller than b * c
, so x
cannot be greater than zero.
Can x
be less than zero? No, by similar argument, left to the reader.
Therefore integer x
is zero, and therefore q1 == q2
.
I'll offer my own proof for fun. This also ignores overflow and only handles positives unfortunately, but I think the proof is clean and clear.
The goal is to show that
floor(floor(x/y)/z) = floor(x/y/z)
where /
is normal division (throughout this proof).
We represent the quotient and remainder of a/b
uniquely as a = kb + r
(by that we mean that k,r
are unique and also note |r| < |b|
). Then we have:
(1) floor(x/y) = k => x = ky + r
(2) floor(floor(x/y)/r) = k1 => floor(x/y) = k1*z + r1
(3) floor(x/y/z) = k2 => x/y = k2*z + r2
So our goal is just to show that k1 == k2
. Well we have:
k1*z + r1 = floor(x/y) = k = (x-r)/y (from lines 1 and 2)
=> x/y - r/y = k1*z + r1 => x/y = k1*z + r1 + r/y
and thus:
(4) x/y = k1*z + r1 + r/y (from above)
x/y = k2*z + r2 (from line 3)
Now observe from (2) that r1
is an integer (for k1*z
is an integer by definition) and r1 < z
(also by definition). Furthermore from (1) we know that r < y => r/y < 1
. Now consider the sum r1 + r/y
from (4). The claim is that r1 + r/y < z
and this is clear from the previous claims (because 0 <= r1 < z
and r1
is an integer so we have 0 <= r1 <= z-1
. Therefore 0 <= r1 + r/y < z
). Thus r1 + r/y = r2
by definition of r2
(otherwise there would be two remainders from x/y
which contradicts the definition of remainder). Hence we have:
x/y = k1*z + r2
x/y = k2*z + r2
and we have our desired conclusion that k1 = k2
.
The above proof should work with negatives except for a couple steps that you'd need to check an extra case(s)... but I didn't check.
If the absolute values of b
and c
are below about sqrt(2^31)
(approx. 46 300), so that b * c
will never overflow, the values will always match. If b * c
overflows, then an error can be thrown in a checked
context, or you can get an incorrect value in an unchecked
context.
Let \
denote integer division (the C# /
operator between two int
s) and let /
denote usual math division. Then, if x,y,z
are positive integers and we are ignoring overflow,
(x \ y) \ z
= floor(floor(x / y) / z) [1]
= floor((x / y) / z) [2]
= floor(x / (y * z))
= x \ (y * z)
where
a \ b = floor(a / b)
The jump from line [1]
to line [2]
above is explained as follows. Suppose you have two integers a
and b
and a fractional number f
in the range [0, 1)
. It is straightforward to see that
floor(a / b) = floor((a + f) / b) [3]
If in line [1]
you identify a = floor(x / y)
, f = (x / y) - floor(x / y)
, and b = z
, then [3]
implies that [1]
and [2]
are equal.
You can generalize this proof to negative integers (still ignoring overflow), but I'll leave that to the reader to keep the point simple.
On the issue of overflow - see Eric Lippert's answer for a good explanation! He also takes a much more rigorous approach in his blog post and answer, something you should look into if you feel I'm being too hand-wavy.
Avoiding the overflow errors noticed by others, they always match.
Let's suppose that a/b=q1
, which means that a=b*q1+r1
, where 0<=r1<b
.
Now suppose that a/b/c=q2
, which means that q1=c*q2+r2
, where 0<=r2<c
.
This means that a=b(c*q2+r2)+r1=b*c*q2+br2+r1
.
In order for a/(b*c)=a/b/c=q2
, we need to have 0<=b*r2+r1<b*c
.
But b*r2+r1<b*r2+b=b*(r2+1)<=b*c
, as required, and the two operations match.
This doesn't work if b
or c
are negative, but I don't know how integer division works in that case either.
counter example: INT_MIN / -1 / 2