简体   繁体   中英

Rounding integer to nearest multiple of another integer

I need to round integers to be the nearest multiple of another integer. Examples for results in the case of multiples of 100:

  • 36->0
  • 99->100
  • 123->100
  • 164->200

and so on.

I came up with the following code, that works, but feels "dirty":

int RoundToMultiple(int toRound, int multiple)
{
    return (toRound + (multiple / 2)) / multiple * multiple;
}

This counts on the truncating properties of integer division to make it work. Can I count on this code to be portable? Are there any compiler setups where this will fail to give me the desired result? If there are, how can I achieve the same results in a portable way?

If needed for a better answer, it can be assumed that multiples will be powers of 10 (including multiples of 1). Numbers can also be assumed to all be positive.

Yes, you can count on this code to be portable. N4296 (which is the latest open draft of C++14) says in section 5.6 [expr.mul]:

For integral operands the / operator yields the algebraic quotient with any fractional part discarded. [Footnote: This is often called truncation towards zero]

This is not a new feature of the latest C++, it could be relied on in C89 too.

The only caveat, is that if toRound is negative , you need to subtract the offset.

An alternative approach is:

int RoundToMultiple(int toRound, int multiple)
{
    const auto ratio = static_cast<double>(toRound) / multiple;
    const auto iratio = std::lround(ratio);
    return iratio * multiple;
}

This avoid messy +/- offsets, but performance will be worse, and there are problems if toRound is so large that it can't be held precisely in a double. (OTOH, if this is for output, then I suspect multiple will be similarly large in this case, so you will be alright.)

The C++ standard explicitly specifies the behavior of integer division thusly:

[expr.mul]

For integral operands the / operator yields the algebraic quotient with any fractional part discarded.

Aka truncation towards zero. This is as portable as it gets.

Though - as mentioned by others - the integral division behaves as you expect, may be the following solution looks "less wired" (still opinion based).

Concerning a solution that converts an int to a double: I personally feel that this is to expensive just for the sake of rounding, but maybe someone can convince me that my feeling is wrong;

Anyway, by using just integral operators, the following solution makes the discussion on whether a double 's mantissa can always hold every int superfluous:

int RoundToMultiple(int toRound, int multiple) {
    toRound += multiple / 2;
    return toRound - (toRound%multiple);
}

If you also wanted to include negative values, the code could be slightly adapted as follows (including tests):

#include <stdio.h>

int RoundToMultiple(int toRound, int multiple) {
    toRound += toRound < 0 ? -multiple / 2 : multiple / 2;
    return toRound - (toRound%multiple);
}

int main(int argc, char const *argv[])
{
    int tests[] = { 36,99,123,164,-36,-99,-123,-164,0 };
    int expectedResults[] = { 0,100,100,200,0,-100,-100,-200,0 };

    int i=0;
    int test=0, result=0, expectedResult=0;
    do {
        test = tests[i];
        result = RoundToMultiple(test, 100);
        expectedResult = expectedResults[i];
        printf("test %d: %d==%d ? %s\n", test, result, expectedResult, (expectedResult==result ? "OK" : "NOK!"));
        i++;
    }
    while(test != 0);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM