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

Rounding integer to nearest multiple of another integer

本文关键字:整数 最接近 另一个 舍入      更新时间:2023-10-16

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

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

等等。

我想出了以下代码,它可以工作,但感觉"脏":

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

这依靠整数除法的截断属性来使其工作。我可以指望此代码是可移植的吗?是否有任何编译器设置无法为我提供所需的结果?如果有,我怎样才能以便携式方式获得相同的结果?

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

是的,您可以指望此代码是可移植的。 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会同样大,所以你会没事的。

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

[扩展]

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

又名截断为零。这是便携式的。

虽然 -

正如其他人提到的 - 积分除法的行为符合您的预期,可能是以下解决方案看起来"不太有线"(仍然基于意见(。

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

无论如何,通过仅使用积分算子,下面的解决方案使得关于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 ? %sn", test, result, expectedResult, (expectedResult==result ? "OK" : "NOK!"));
        i++;
    }
    while(test != 0);
}