In C# integer arithmetic, does a/b/c always equal a/(b*c)?

前端 未结 6 1238
半阙折子戏
半阙折子戏 2021-02-01 11:39

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,          


        
相关标签:
6条回答
  • 2021-02-01 12:13

    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.

    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2021-02-01 12:22

    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.

    0 讨论(0)
  • 2021-02-01 12:26

    Let \ denote integer division (the C# / operator between two ints) 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.

    0 讨论(0)
  • 2021-02-01 12:27

    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.

    0 讨论(0)
  • 2021-02-01 12:34

    counter example: INT_MIN / -1 / 2

    0 讨论(0)
提交回复
热议问题