代码之家  ›  专栏  ›  技术社区  ›  Robben_Ford_Fan_boy

c++:舍入到一个数字的最近倍数

  •  144
  • Robben_Ford_Fan_boy  · 技术社区  · 14 年前

    好吧-我在这里发布这个(如果有人投票结束的话我会删除),这看起来像是一个基本问题。

    在C++中,这是一个正确的方法吗?

    我知道还有其他相关的问题,但我特别感兴趣的是知道什么是最好的方式在C++中做到这一点:

    int roundUp(int numToRound, int multiple)
    {
     if(multiple == 0)
     {
      return numToRound;
     }
    
     int roundDown = ( (int) (numToRound) / multiple) * multiple;
     int roundUp = roundDown + multiple; 
     int roundCalc = roundUp;
     return (roundCalc);
    }
    

    更新: 抱歉,我可能没有明确表示意图。以下是一些例子:

    roundUp(7, 100)
    //return 100
    
    roundUp(117, 100)
    //return 200
    
    roundUp(477, 100)
    //return 500
    
    roundUp(1077, 100)
    //return 1100
    
    roundUp(52, 20)
    //return 60
    
    roundUp(74, 30)
    //return 90
    

    编辑:谢谢所有的回复。我的目的是:

    int roundUp(int numToRound, int multiple)  
    {  
     if(multiple == 0)  
     {  
      return numToRound;  
     }  
    
     int remainder = numToRound % multiple; 
     if (remainder == 0)
      {
        return numToRound; 
      }
    
     return numToRound + multiple - remainder; 
    }  
    
    28 回复  |  直到 5 年前
        1
  •  141
  •   bdesham    9 年前

    这适用于正数,不确定负数。它只使用整数数学。

    int roundUp(int numToRound, int multiple)
    {
        if (multiple == 0)
            return numToRound;
    
        int remainder = numToRound % multiple;
        if (remainder == 0)
            return numToRound;
    
        return numToRound + multiple - remainder;
    }
    

    编辑:这里有一个使用负数的版本,如果“向上”表示结果总是>=输入。

    int roundUp(int numToRound, int multiple)
    {
        if (multiple == 0)
            return numToRound;
    
        int remainder = abs(numToRound) % multiple;
        if (remainder == 0)
            return numToRound;
    
        if (numToRound < 0)
            return -(abs(numToRound) - remainder);
        else
            return numToRound + multiple - remainder;
    }
    
        2
  •  92
  •   KindDragon    7 年前

    无条件:

    int roundUp(int numToRound, int multiple) 
    {
        assert(multiple);
        return ((numToRound + multiple - 1) / multiple) * multiple;
    }
    

    这类作品 rounding away from zero 对于负数

    编辑:也适用于负数的版本

    int roundUp(int numToRound, int multiple) 
    {
        assert(multiple);
        int isPositive = (int)(numToRound >= 0);
        return ((numToRound + isPositive * (multiple - 1)) / multiple) * multiple;
    }
    

    Tests


    如果 multiple 是2的力量

    int roundUp(int numToRound, int multiple) 
    {
        assert(multiple && ((multiple & (multiple - 1)) == 0));
        return (numToRound + multiple - 1) & -multiple;
    }
    

    Tests

        3
  •  33
  •   Mark Lakata    6 年前

    当系数始终为正时,此方法有效:

    int round_up(int num, int factor)
    {
        return num + factor - 1 - (num - 1) % factor;
    }
    

    编辑:返回 round_up(0,100)=100 . 有关返回的解决方案,请参阅下面保罗的评论 round_up(0,100)=0 .

        4
  •  22
  •   plinth    14 年前

    这是“如何找出N位需要多少字节”问题的概括。(A:(n位+7)/8)。

    int RoundUp(int n, int roundTo)
    {
        // fails on negative?  What does that mean?
        if (roundTo == 0) return 0;
        return ((n + roundTo - 1) / roundTo) * roundTo; // edit - fixed error
    }
    
        5
  •  14
  •   doron    14 年前
    int roundUp(int numToRound, int multiple)
    {
     if(multiple == 0)
     {
      return 0;
     }
     return ((numToRound - 1) / multiple + 1) * multiple;  
    }
    

    也不必乱碰环境

        6
  •  9
  •   Mr. Polywhirl    9 年前
    float roundUp(float number, float fixedBase) {
        if (fixedBase != 0 && number != 0) {
            float sign = number > 0 ? 1 : -1;
            number *= sign;
            number /= fixedBase;
            int fixedPoint = (int) ceil(number);
            number = fixedPoint * fixedBase;
            number *= sign;
        }
        return number;
    }
    

    这适用于任何浮点数或基数(例如,可以将-4四舍五入到最接近的6.75)。本质上,它是转换到固定点,在那里四舍五入,然后再转换回来。它通过舍入0来处理负片。它还通过将函数转换为舍入来处理负值舍入。

    特定于int的版本如下:

    int roundUp(int number, int fixedBase) {
        if (fixedBase != 0 && number != 0) {
            int sign = number > 0 ? 1 : -1;
            int baseSign = fixedBase > 0 ? 1 : 0;
            number *= sign;
            int fixedPoint = (number + baseSign * (fixedBase - 1)) / fixedBase;
            number = fixedPoint * fixedBase;
            number *= sign;
        }
        return number;
    }
    

    这或多或少是普林特的答案,加上负输入支持。

        7
  •  8
  •   aaron-bond    11 年前

    对于任何寻求简短而甜蜜答案的人。这是我用过的。没有否定的解释。

    n - (n % r)
    

    这将返回上一个因子。

    (n + r) - (n % r)
    

    将返回下一个。希望这能帮助别人。:)

        8
  •  7
  •   Flovdis    10 年前

    这是一个现代的C++方法,它使用了一个模板函数,它用于浮点、双、长、int和短(但不是长的长,因为使用了双值而长了一倍)。

    #include <cmath>
    #include <iostream>
    
    template<typename T>
    T roundMultiple( T value, T multiple )
    {
        if (multiple == 0) return value;
        return static_cast<T>(std::round(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
    }
    
    int main()
    {
        std::cout << roundMultiple(39298.0, 100.0) << std::endl;
        std::cout << roundMultiple(20930.0f, 1000.0f) << std::endl;
        std::cout << roundMultiple(287399, 10) << std::endl;
    }
    

    但您可以轻松地添加对 long long long double 模板专业化如下:

    template<>
    long double roundMultiple<long double>( long double value, long double multiple)
    {
        if (multiple == 0.0l) return value;
        return std::round(value/multiple)*multiple;
    }
    
    template<>
    long long roundMultiple<long long>( long long value, long long multiple)
    {
        if (multiple == 0.0l) return value;
        return static_cast<long long>(std::round(static_cast<long double>(value)/static_cast<long double>(multiple))*static_cast<long double>(multiple));
    }
    

    若要创建要汇总的函数,请使用 std::ceil 并且总是取整使用 std::floor . 我上面的例子是四舍五入使用 std::round .

    创建“Round up”或更著名的“Round天花板”模板功能,如下所示:

    template<typename T>
    T roundCeilMultiple( T value, T multiple )
    {
        if (multiple == 0) return value;
        return static_cast<T>(std::ceil(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
    }
    

    创建“Round down”或更确切地说是“Round floor”模板功能,如下所示:

    template<typename T>
    T roundFloorMultiple( T value, T multiple )
    {
        if (multiple == 0) return value;
        return static_cast<T>(std::floor(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
    }
    
        9
  •  5
  •   Mike Caron    14 年前

    首先,您的错误条件(multiple==0)应该有一个返回值。什么?我不知道。也许你想提出一个例外,这取决于你自己。但是,什么也不归还是危险的。

    其次,您应该检查numtoround是否已经是一个倍数。否则,当您添加 multiple roundDown 你会得到错误的答案。

    第三,你的石膏是错误的。你投 numToRound 一个整数,但它已经是一个整数。您需要在除法之前强制转换为double,在乘法之后强制转换为int。

    最后一点,你想要负数做什么?四舍五入“向上”可以表示四舍五入为零(与正数同向四舍五入),也可以表示远离零(较大的负数)。或者,也许你不在乎。

    下面是前三个修复的版本,但我不处理负面问题:

    int roundUp(int numToRound, int multiple)
    {
     if(multiple == 0)
     {
      return 0;
     }
     else if(numToRound % multiple == 0)
     {
      return numToRound
     }
    
     int roundDown = (int) (( (double) numToRound / multiple ) * multiple);
     int roundUp = roundDown + multiple; 
     int roundCalc = roundUp;
     return (roundCalc);
    }
    
        10
  •  4
  •   Community noseratio    7 年前

    四舍五入为二:

    以防任何人需要一个将正数四舍五入到二次方的最接近倍数的解决方案(因为我就是这样结束的):

    // number: the number to be rounded (ex: 5, 123, 98345, etc.)
    // pow2:   the power to be rounded to (ex: to round to 16, use '4')
    int roundPow2 (int number, int pow2) {
        pow2--;                     // because (2 exp x) == (1 << (x -1))
        pow2 = 0x01 << pow2;
    
        pow2--;                     // because for any
                                    //
                                    // (x = 2 exp x)
                                    //
                                    // subtracting one will
                                    // yield a field of ones
                                    // which we can use in a
                                    // bitwise OR
    
        number--;                   // yield a similar field for
                                    // bitwise OR
        number = number | pow2;
        number++;                   // restore value by adding one back
    
        return number;
    }
    

    如果输入的数字已经是一个倍数,它将保持不变。

    下面是gcc提供的x86_64输出 -O2 -Os (2013年9月9日建造-Godbolt GCC在线):

    roundPow2(int, int):
        lea ecx, [rsi-1]
        mov eax, 1
        sub edi, 1
        sal eax, cl
        sub eax, 1
        or  eax, edi
        add eax, 1
        ret
    

    每个C代码行与其程序集中的行完全对应: http://goo.gl/DZigfX

    每一条指令都是 极快 ,所以功能也非常快。因为代码很小很快,所以 inline 使用时的函数。


    信用:

        11
  •  3
  •   the swine    7 年前

    我正在使用:

    template <class _Ty>
    inline _Ty n_Align_Up(_Ty n_x, _Ty n_alignment)
    {
        assert(n_alignment > 0);
        //n_x += (n_x >= 0)? n_alignment - 1 : 1 - n_alignment; // causes to round away from zero (greatest absolute value)
        n_x += (n_x >= 0)? n_alignment - 1 : -1; // causes to round up (towards positive infinity)
        //n_x += (_Ty(-(n_x >= 0)) & n_alignment) - 1; // the same as above, avoids branch and integer multiplication
        //n_x += n_alignment - 1; // only works for positive numbers (fastest)
        return n_x - n_x % n_alignment; // rounds negative towards zero
    }
    

    对于两种权力:

    template <class _Ty>
    bool b_Is_POT(_Ty n_x)
    {
        return !(n_x & (n_x - 1));
    }
    
    template <class _Ty>
    inline _Ty n_Align_Up_POT(_Ty n_x, _Ty n_pot_alignment)
    {
        assert(n_pot_alignment > 0);
        assert(b_Is_POT(n_pot_alignment)); // alignment must be power of two
        -- n_pot_alignment;
        return (n_x + n_pot_alignment) & ~n_pot_alignment; // rounds towards positive infinity (i.e. negative towards zero)
    }
    

    请注意,这两个圆负值都是零(这意味着对所有值舍入到正无穷大),它们都不依赖于符号溢出(在C/C++中是未定义的)。

    这给出:

    n_Align_Up(10, 100) = 100
    n_Align_Up(110, 100) = 200
    n_Align_Up(0, 100) = 0
    n_Align_Up(-10, 100) = 0
    n_Align_Up(-110, 100) = -100
    n_Align_Up(-210, 100) = -200
    n_Align_Up_POT(10, 128) = 128
    n_Align_Up_POT(130, 128) = 256
    n_Align_Up_POT(0, 128) = 0
    n_Align_Up_POT(-10, 128) = 0
    n_Align_Up_POT(-130, 128) = -128
    n_Align_Up_POT(-260, 128) = -256
    
        12
  •  2
  •   Martin Beckett    14 年前

    可能更安全的方法是强制转换为float并使用ceil()—除非您知道int除法将产生正确的结果。

        13
  •  2
  •   Michal Ciechan    14 年前
    int noOfMultiples = int((numToRound / multiple)+0.5);
    return noOfMultiples*multiple
    

    C++将每一个数字向下舍入,因此如果你加0.5(如果它的1.5将是2),但是1.49将是1.99,因此1。

    编辑-很抱歉,没有看到您想要进行四舍五入,我建议使用ceil()方法而不是+0.5

        14
  •  2
  •   Jesse Naugher    14 年前

    好吧,首先,因为我不太明白你想做什么,台词

    int roundUp = roundDown + multiple;
    int roundCalc = roundUp;
    return (roundCalc); 
    

    当然可以缩短到

    int roundUp = roundDown + multiple;
    return roundUp;
    
        15
  •  2
  •   pb2q    11 年前

    可能这有助于:

    int RoundUpToNearestMultOfNumber(int val, int num)
    {
      assert(0 != num);
      return (floor((val + num) / num) * num);
    }
    
        16
  •  2
  •   onmyway133    11 年前

    总是把事情搞得一团糟

    int alwaysRoundUp(int n, int multiple)
    {
        if (n % multiple != 0) {
            n = ((n + multiple) / multiple) * multiple;
    
            // Another way
            //n = n - n % multiple + multiple;
        }
    
        return n;
    }
    

    始终打开(1,10)->10

    始终打开(5,10)->10

    始终打开(10,10)->10


    四舍五入

    int alwaysRoundDown(int n, int multiple)
    {
        n = (n / multiple) * multiple;
    
        return n;
    }
    

    始终向下(1,10)->0

    始终向下(5,10)->0

    始终向下(10,10)->10


    以正常方式取整

    int normalRound(int n, int multiple)
    {
        n = ((n + multiple/2)/multiple) * multiple;
    
        return n;
    }
    

    正圆形(1,10)->0

    正圆形(5,10)->10

    正圆形(10,10)->10

        17
  •  1
  •   Joshua Wade    13 年前

    我发现了一个类似于上面发布的算法:

    int[(x+n-1)/n]*[(n x)/x],其中x是用户输入值,n是正在使用的倍数。

    它适用于所有值x,其中x是整数(正或负,包括零)。我专门为C++程序编写的,但基本上可以用任何语言来实现。

        18
  •  1
  •   user990343    11 年前

    对于负数:

    这应该很容易做到,但标准的模%运算符并不像人们预期的那样处理负数。例如-14%12=-2而不是10。首先要做的是得到从不返回负数的模运算符。那么,汇总就非常简单了。

    public static int mod(int x, int n) 
    {
        return ((x % n) + n) % n;
    }
    
    public static int roundUp(int numToRound, int multiple) 
    {
        return numRound + mod(-numToRound, multiple);
    }
    
        19
  •  1
  •   Gotcha    10 年前

    这就是我要做的:

    #include <cmath>
    
    int roundUp(int numToRound, int multiple)
    {
        // if our number is zero, return immediately
       if (numToRound == 0)
            return multiple;
    
        // if multiplier is zero, return immediately
        if (multiple == 0)
            return numToRound;
    
        // how many times are number greater than multiple
        float rounds = static_cast<float>(numToRound) / static_cast<float>(multiple);
    
        // determine, whether if number is multiplier of multiple
        int floorRounds = static_cast<int>(floor(rounds));
    
        if (rounds - floorRounds > 0)
            // multiple is not multiplier of number -> advance to the next multiplier
            return (floorRounds+1) * multiple;
        else
            // multiple is multiplier of number -> return actual multiplier
            return (floorRounds) * multiple;
    }
    

    代码可能不是最佳的,但我更喜欢干净的代码而不是干的性能。

        20
  •  1
  •   user3392484    10 年前
    int roundUp (int numToRound, int multiple)
    {
      return multiple * ((numToRound + multiple - 1) / multiple);
    }
    

    虽然:

    • 不适用于负数
    • 如果numRound+多个溢出将不起作用

    建议改用无符号整数,它定义了溢出行为。

    您将得到一个异常multiple==0,但在这种情况下,它并不是一个定义良好的问题。

        21
  •  1
  •   nhed    10 年前

    丙:

    int roundUp(int numToRound, int multiple)
    {
      return (multiple ? (((numToRound+multiple-1) / multiple) * multiple) : numToRound);
    }
    

    对于您的~/.bashrc:

    roundup()
    {
      echo $(( ${2} ? ((${1}+${2}-1)/${2})*${2} : ${1} ))
    }
    
        22
  •  1
  •   Mira Weller    7 年前

    如果 x 已经是一个倍数:

    int round_up(int x, int div)
    {
        return x + (div - x % div) % div;
    }
    

    我们找到余数的倒数,然后用除数的模再次使其无效,如果它是除数本身,则加上 X .

    round_up(19, 3) = 21
    
        23
  •  1
  •   weatx    6 年前

    这是我的解决方案,基于OP的建议,以及其他人给出的例子。因为大多数人都希望它能处理负数,所以这个解决方案就是这样做的,而不需要使用任何特殊的函数,例如abs等。

    通过避免使用模量并使用除法,负数是一个自然的结果,尽管它是四舍五入的。在计算完向下取整的版本后,它将进行所需的数学取整,无论是负方向还是正方向。

    同样要注意的是,没有使用特殊的函数来计算任何东西,所以这里有一个小的速度提升。

    int RoundUp(int n, int multiple)
    {
        // prevent divide by 0 by returning n
        if (multiple == 0) return n;
    
        // calculate the rounded down version
        int roundedDown = n / multiple * multiple;
    
        // if the rounded version and original are the same, then return the original
        if (roundedDown == n) return n;
    
        // handle negative number and round up according to the sign
        // NOTE: if n is < 0 then subtract the multiple, otherwise add it
        return (n < 0) ? roundedDown - multiple : roundedDown + multiple;
    }
    
        24
  •  1
  •   Anne Quinn    5 年前

    四舍五入到最接近的倍数,正好是2的幂

    unsigned int round(unsigned int value, unsigned int multiple){
        return ((value-1u) & ~(multiple-1u)) + multiple;
    }
    

    这对于沿缓存线分配时很有用,因为您需要的舍入增量是2的幂,但结果值只需要是它的倍数。论 gcc 此函数的主体生成8个不带任何除法或分支的汇编指令。

    round(  0,  16) ->   0
    round(  1,  16) ->  16
    round( 16,  16) ->  16
    round(257, 128) -> 384 (128 * 3)
    round(333,   2) -> 334
    
        25
  •  0
  •   Adolfo    10 年前
    /// Rounding up 'n' to the nearest multiple of number 'b'.
    /// - Not tested for negative numbers.
    /// \see http://stackoverflow.com/questions/3407012/
    #define roundUp(n,b) ( (b)==0 ? (n) : ( ((n)+(b)-1) - (((n)-1)%(b)) ) )
    
    /// \c test->roundUp().
    void test_roundUp() {   
        // yes_roundUp(n,b) ( (b)==0 ? (n) : ( (n)%(b)==0 ? n : (n)+(b)-(n)%(b) ) )
        // yes_roundUp(n,b) ( (b)==0 ? (n) : ( ((n + b - 1) / b) * b ) )
    
        // no_roundUp(n,b) ( (n)%(b)==0 ? n : (b)*( (n)/(b) )+(b) )
        // no_roundUp(n,b) ( (n)+(b) - (n)%(b) )
    
    if (true) // couldn't make it work without (?:)
    {{  // test::roundUp()
        unsigned m;
                    { m = roundUp(17,8); } ++m;
        assertTrue( 24 == roundUp(17,8) );
                    { m = roundUp(24,8); }
        assertTrue( 24 == roundUp(24,8) );
    
        assertTrue( 24 == roundUp(24,4) );
        assertTrue( 24 == roundUp(23,4) );
                    { m = roundUp(23,4); }
        assertTrue( 24 == roundUp(21,4) );
    
        assertTrue( 20 == roundUp(20,4) );
        assertTrue( 20 == roundUp(19,4) );
        assertTrue( 20 == roundUp(18,4) );
        assertTrue( 20 == roundUp(17,4) );
    
        assertTrue( 17 == roundUp(17,0) );
        assertTrue( 20 == roundUp(20,0) );
    }}
    }
    
        26
  •  0
  •   Dave    10 年前

    这将得到您正在查找的正整数的结果:

    #include <iostream>
    using namespace std;
    
    int roundUp(int numToRound, int multiple);
    
    int main() {
        cout << "answer is: " << roundUp(7, 100) << endl;
        cout << "answer is: " << roundUp(117, 100) << endl;
        cout << "answer is: " << roundUp(477, 100) << endl;
        cout << "answer is: " << roundUp(1077, 100) << endl;
        cout << "answer is: " << roundUp(52,20) << endl;
        cout << "answer is: " << roundUp(74,30) << endl;
        return 0;
    }
    
    int roundUp(int numToRound, int multiple) {
        if (multiple == 0) {
            return 0;
        }
        int result = (int) (numToRound / multiple) * multiple;
        if (numToRound % multiple) {
            result += multiple;
        } 
        return result;
    }
    

    输出结果如下:

    answer is: 100
    answer is: 200
    answer is: 500
    answer is: 1100
    answer is: 60
    answer is: 90
    
        27
  •  0
  •   Neel    6 年前

    我认为这对你有帮助。我已经用C语言编写了下面的程序。

    # include <stdio.h>
    int main()
    {
      int i, j;
      printf("\nEnter Two Integers i and j...");
      scanf("%d %d", &i, &j);
      int Round_Off=i+j-i%j;
      printf("The Rounded Off Integer Is...%d\n", Round_Off);
      return 0;
    }
    
        28
  •  -1
  •   user1100538    12 年前

    这对我有用,但没有尝试处理否定

    public static int roundUp(int numToRound, int multiple) {
        if (multiple == 0) {
            return 0;
        } else if (numToRound % multiple == 0) {
        return numToRound;
        }
    
        int mod = numToRound % multiple;
        int diff = multiple - mod;
        return numToRound + diff;
    }