Eyal K.
Eyal K.

Reputation: 1072

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:

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.

Upvotes: 7

Views: 2122

Answers (3)

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.)

Upvotes: 4

Stephan Lechner
Stephan Lechner

Reputation: 35164

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);
}

Upvotes: 0

Sam Varshavchik
Sam Varshavchik

Reputation: 118435

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.

A.k.a. truncation towards zero. This is as portable as it gets.

Upvotes: 2

Related Questions