将整数舍入到另一个整数的最接近倍数

时间:2017-05-22 15:30:29

标签: c++

我需要将整数舍入为另一个整数的最接近的倍数。在100的倍数的情况下结果的示例:

  • 36-大于0
  • 99-→100
  • 123-大于100
  • 164-> 200

等等。

我提出了以下代码,但是感觉“很脏”:

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

这依赖于整数除法的截断属性以使其有效。 我可以指望这个代码是可移植的吗?是否有任何编译器设置,这将无法给我所需的结果?如果有,我怎样才能以便携的方式获得相同的结果?

如果需要更好的答案,可以假设倍数将是10的幂(包括1的倍数)。数字也可以假设都是正数。

3 个答案:

答案 0 :(得分:4)

是的,您可以指望此代码可移植。 N4296(这是C ++ 14的最新公开草案)在第5.6节[expr.mul]中说:

  

有关   积分操作数/运算符产生代数商,丢弃任何小数部分。 [脚注:这通常被称为截断为零]

这不是最新C ++的新功能,它也可以在C89中使用。

唯一需要注意的是,如果toRound 否定,则需要减去偏移量。

另一种方法是:

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

这可以避免凌乱的+/-偏移,但性能会更差,如果toRound如此之大以至于无法精确地保持在双精度中,则会出现问题。 (OTOH,如果这是输出,那么我怀疑multiple在这种情况下会同样大,所以你会没事的。)

答案 1 :(得分:2)

C ++标准明确地指定了整数除法的行为:

  

[expr.mul]

     

对于积分操作数,/运算符产生代数   弃去任何小数部分的商。

A.k.a。截断为零。这是便携式的。

答案 2 :(得分:0)

虽然 - 正如其他人所说 - 积分除法表现得如你所愿,可能是以下解决方案看起来&#34;更少有线&#34; (仍以意见为基础)。

关于将int转换为double的解决方案:我个人觉得这只是为了四舍五入而昂贵,但也许有人可以说服我,我的感觉是错的;

无论如何,通过仅使用整数运算符,以下解决方案讨论了double的尾数是否总能保持每个int多余:

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

如果您还想包含负值,可以按如下方式(包括测试)略微调整代码:

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