C ++: округление до ближайшего кратного числа - PullRequest
148 голосов
/ 04 августа 2010

ОК - я почти смущен, публикуя это здесь (и я буду удалять, если кто-то проголосует за закрытие), поскольку это кажется основным вопросом.

Это правильный способ округления до кратного числа в 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

Ответы [ 28 ]

3 голосов
/ 15 октября 2014

Я использую:

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
2 голосов
/ 04 августа 2010
int noOfMultiples = int((numToRound / multiple)+0.5);
return noOfMultiples*multiple

C ++ округляет каждое число вниз, поэтому, если вы добавите 0,5 (если его 1,5, то будет 2), но 1,49 будет 1,99, следовательно, 1.

РЕДАКТИРОВАТЬ - Извините, я не увидел, что вы хотите округлить, я бы предложил использовать метод ceil () вместо + 0.5

2 голосов
/ 04 августа 2010

хорошо с одной стороны, так как я не совсем понимаю, что вы хотите сделать, строки

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

определенно может быть сокращено до

int roundUp = roundDown + multiple;
return roundUp;
2 голосов
/ 20 января 2014

Чтобы всегда округлять вверх

int alwaysRoundUp(int n, int multiple)
{
    if (n % multiple != 0) {
        n = ((n + multiple) / multiple) * multiple;

        // Another way
        //n = n - n % multiple + multiple;
    }

    return n;
}

AlwaysRoundUp (1, 10) -> 10

AlwaysRoundUp (5, 10) -> 10

alwaysRoundUp (10, 10) -> 10


Чтобы всегда округлять вниз

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

    return n;
}

AlwaysRoundDown (1, 10) -> 0

alwaysRoundDown (5, 10) -> 0

AlwaysRoundDown (10, 10) -> 10


Для округления в обычном порядке

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

    return n;
}

normalRound (1, 10) -> 0

normalRound (5, 10) -> 10

normalRound (10, 10) -> 10

2 голосов
/ 20 сентября 2013

может быть это может помочь:

int RoundUpToNearestMultOfNumber(int val, int num)
{
  assert(0 != num);
  return (floor((val + num) / num) * num);
}
2 голосов
/ 02 марта 2019

Округление до ближайшего кратного, которое оказывается степенью 2

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

Это может быть полезно при распределении по кеш-линиям, где желаемый прирост округления является степенью двойки, но результирующее значение должно быть только кратным. На gcc тело этой функции генерирует 8 инструкций по сборке без деления или ветвей.

round(  0,  16) ->   0
round(  1,  16) ->  16
round( 16,  16) ->  16
round(257, 128) -> 384 (128 * 3)
round(333,   2) -> 334
2 голосов
/ 04 августа 2010

Вероятно, безопаснее разыгрывать с плавающей точкой и использовать ceil () - если только вы не знаете, что деление int даст правильный результат.

1 голос
/ 28 июля 2018

Вот мое решение, основанное на предложении ОП и примерах, приведенных всеми остальными.Поскольку большинство всех искало его для обработки отрицательных чисел, это решение делает именно это без использования каких-либо специальных функций, например, 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;
}
1 голос
/ 26 января 2011

Я нашел алгоритм, который несколько похож на тот, что был опубликован выше:

int [(| x | + n-1) / n] * [(nx) / | x |], где xпользовательское значение, а n - это кратное число.

Работает для всех значений x, где x - целое число (положительное или отрицательное, включая ноль).Я написал это специально для программы на C ++, но в принципе это можно реализовать на любом языке.

1 голос
/ 26 мая 2016

Я использую комбинацию модулей, чтобы аннулировать добавление остатка, если x уже кратно:

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

Мы находим обратное значение остатка, а затем модуль, который с делителем снова обнуляетесли это сам делитель, то добавьте x.

round_up(19, 3) = 21
...