简体   繁体   English

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

[英]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: 在100的倍数的情况下结果的示例:

  • 36->0 36-> 0
  • 99->100 99-> 100
  • 123->100 123-> 100
  • 164->200 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). 如果需要更好的答案,可以假设倍数将是10的幂(包括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]: N4296 (这是C ++ 14的最新公开草案)在第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. 这不是最新C ++的新功能,它也可以在C89中使用。

The only caveat, is that if toRound is negative , you need to subtract the offset. 唯一需要注意的是,如果toRound负数 ,则需要减去偏移量。

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. 这样可以避免凌乱的+/-偏移,但性能会更差,如果toRound太大而无法精确地保持双倍,则会出现问题。 (OTOH, if this is for output, then I suspect multiple will be similarly large in this case, so you will be alright.) (OTOH,如果这是输出,那么我怀疑在这种情况下multiple同样大,所以你会没事的。)

The C++ standard explicitly specifies the behavior of integer division thusly: C ++标准明确地指定了整数除法的行为:

[expr.mul] [expr.mul]

For integral operands the / operator yields the algebraic quotient with any fractional part discarded. 对于积分操作数,/运算符产生代数商,丢弃任何小数部分。

Aka truncation towards zero. Aka截断为零。 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; 关于将int转换为double的解决方案:我个人觉得这只是为了四舍五入而昂贵,但也许有人可以说服我,我的感觉是错的;

Anyway, by using just integral operators, the following solution makes the discussion on whether a double 's mantissa can always hold every int superfluous: 无论如何,通过仅使用整数运算符,以下解决方案讨论了double的尾数是否总能保持每个int多余:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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