Search code examples
cintegerdivisioninteger-divisioninteger-arithmetic

How to divide a nonnegative variable integer by a constant fixed-point rational โ‰ฅ1 without overflow (at all, if possible)?


How to compute โŒŠ๐‘ฅรท๐‘โŒ‹ for a variable integer ๐‘ฅ and a fixed-point rational number ๐‘โ‰ฅ1 precisely? The input ๐‘ฅ โˆˆ [0, INT_MAX] is stored in a variable of type int, ๐‘ โˆˆ [1, INT_MAX] is given as a decimal literal with a decimal separator in the form ๐‘‘๐‘‘๐‘‘๐‘‘๐‘‘.๐‘‘๐‘‘ (you may alternatively represent it as a fraction, say, ๐‘‘๐‘‘๐‘‘๐‘‘๐‘‘๐‘‘๐‘‘/100, if this helps, or as a product of prime powers divided by another product of prime powers), and the output should be a value of type int. Here, รท is mathematical division. Our running example is โŒŠ๐‘ฅรท65781.76โŒ‹; within this example, you can assume that int is at least 3 bytes wide (as otherwise the result would be trivially 0). How to do this in C properly?

Clearly, (int)(๐‘ฅ/65781.76f) would involve floating-point arithmetic (and, according to http://www.h-schmidt.net/FloatConverter/IEEE754.html, 65781.76, as most constants, is not precisely representable at least on my machine), whereas 100*๐‘ฅ/6578176L and 25*๐‘ฅ/1644544L risk an overflow too much. In any case, to my knowledge, adding the suffix L or LL (as in, e.g., 25LL*๐‘ฅ/1644544L) is pointless because the types long long, long, and int are not guaranteed to be of different widths. Any other options? I'm interested in solutions involving integer arithmetic only and (most importantly) a proper explanation.

Ideally, we'd like to have a precise and portable solution; if possible, let's stick to C89/C90 without external libraries.


Solution

  • Specific Number

    For the specific number in the question, we want โŒŠ๐‘ฅ/65781.76โŒ‹ = โŒŠ๐‘ฅโ€ข25/(2048โ€ข803)โŒ‹ = โŒŠ๐‘ฅโ€ข(16+8+1)/2048 / 803โŒ‹ = โŒŠ(๐‘ฅ/128 + ๐‘ฅ/256 + ๐‘ฅ/2048) / 803โŒ‹.

    Next, we separate the integer and fraction portions of ๐‘ฅ/128, ๐‘ฅ/256, and ๐‘ฅ/2048: โŒŠ(โŒŠ๐‘ฅ/128โŒ‹ + ๐‘ฅ%128/128 + โŒŠ๐‘ฅ/256โŒ‹ + ๐‘ฅ%256/256 + โŒŠ๐‘ฅ/2048โŒ‹ + x%2048/2048) / 803โŒ‹.

    Then we group the fractions, using โ€œ%โ€ to indicate the remainder operation: โŒŠ(โŒŠ๐‘ฅ/128โŒ‹ + โŒŠ๐‘ฅ/256โŒ‹ + โŒŠ๐‘ฅ/2048โŒ‹ + ๐‘ฅ%128/128 + ๐‘ฅ%256/256 + x%2048/2048) / 803โŒ‹.

    And consolidate them into one term: โŒŠ(โŒŠ๐‘ฅ/128โŒ‹ + โŒŠ๐‘ฅ/256โŒ‹ + โŒŠ๐‘ฅ/2048โŒ‹ + (๐‘ฅ%128โ€ข16 + ๐‘ฅ%256โ€ข8 + x%2048)/2048) / 803โŒ‹.

    Now the only fraction in the sum is in (๐‘ฅ%128โ€ข16 + ๐‘ฅ%256โ€ข8 + x%2048)/2048, so we may discard it, taking its integer portion: โŒŠ(โŒŠ๐‘ฅ/128โŒ‹ + โŒŠ๐‘ฅ/256โŒ‹ + โŒŠ๐‘ฅ/2048โŒ‹ + โŒŠ(๐‘ฅ%128โ€ข16 + ๐‘ฅ%256โ€ข8 + x%2048)/2048โŒ‹) / 803โŒ‹.

    So all parts may be calculated with integer arithmetic: (x/128 + x/256 + x/2048 + (x % 128 * 16 + x % 256 * 8 + x % 2048)/2048) / 803.

    Observe that all multiplications, remainders, and divisions except the division by 803 may be computed with bit shifts and masks.

    General Cases

    The above relies on the fact that the quotient in question had a power of two in its factorization. In general, given p and q with pโ€ข(qโˆ’1) โ‰ค INT_MAX, we can compute โŒŠxโ€ขp/qโŒ‹ with x/q*p + x%q*p/q, because โŒŠxโ€ขp/qโŒ‹ = โŒŠx/qโ€ขpโŒ‹ = โŒŠ(โŒŠx/qโŒ‹+x/qโˆ’โŒŠx/qโŒ‹)โ€ขpโŒ‹ = โŒŠโŒŠx/qโŒ‹โ€ขp + (x/qโˆ’โŒŠx/qโŒ‹)โ€ขpโŒ‹ = โŒŠโŒŠx/qโŒ‹โ€ขp + (x%q/qโŒ‹)โ€ขpโŒ‹ = โŒŠx/qโŒ‹โ€ขp + โŒŠx%q/qโ€ขpโŒ‹ = โŒŠx/qโŒ‹โ€ขp + โŒŠx%qโ€ขp/qโŒ‹.

    Thus, for the number in question, we seek โŒŠxโ€ข25/1,644,544โŒ‹, so we can use x / 1644544 * 25 + x % 1644544 * 25 / 1644544.

    This uses two divisions, one to compute the quotient and remainder of x when divided by q and then a second division by q.