С++: округление до ближайшего кратного числа

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

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

Ответ 1

Это работает для положительных чисел, не уверен в негативе. Он использует только целую математику.

int roundUp(int numToRound, int multiple)
{
    if (multiple == 0)
        return numToRound;

    int remainder = numToRound % multiple;
    if (remainder == 0)
        return numToRound;

    return numToRound + multiple - remainder;
}

Изменить: Здесь версия, которая работает с отрицательными числами, если "вверх" означает результат, который всегдa >= ввод.

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

Без условий:

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

Это работает как округление от нуля для отрицательных чисел

РЕДАКТИРОВАТЬ: версия, которая работает также для отрицательных чисел

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

тесты


Если multiple это степень 2

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

тесты

Ответ 3

Это работает, когда фактор всегда будет положительным:

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

Это обобщение проблемы "как узнать, сколько байтов 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

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

И не нужно возиться с условиями

Ответ 6

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). По сути, он конвертируется в неподвижную точку, округляется туда, а затем возвращается обратно. Он обрабатывает негативы округлением AWAY от 0. Он также обрабатывает отрицательный круг для значения, по существу превращая функцию в roundDown.

Версия, специфичная для 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

Для тех, кто ищет короткий и сладкий ответ. Это то, что я использовал. Без учета негативов.

n - (n % r)

Это вернет предыдущий коэффициент.

(n + r) - (n % r)

Вернет следующий. Надеюсь, это поможет кому-то.:)

Ответ 8

Это современный подход С++, использующий функцию шаблона, которая работает для float, double, long, int и short (но не для длинного и длинного двойных из-за использованных двойных значений).

#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.

Создайте функцию округления или более известную как "круглый потолок", как показано ниже:

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

Создайте функцию округления вниз или более известную как "круглый пол", как показано ниже:

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

Во-первых, ваше условие ошибки (multiple == 0) должно, вероятно, иметь возвращаемое значение. Какие? Я не знаю. Может быть, вы хотите бросить исключение, что до вас. Но возвращение ничего не опасно.

Во-вторых, вы должны проверить, что numToRound еще не кратно. В противном случае, когда вы добавите multiple в roundDown, вы получите неправильный ответ.

В-третьих, ваши броски ошибочны. Вы отбрасываете numToRound в целое число, но это уже целое число. Вам нужно, чтобы бросить, чтобы удвоить перед делением, и вернуться к 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

Радиус действия до двух:

На всякий случай, если кто-то нуждается в решении для положительных чисел, округленных до ближайшего кратного мощности, равного двум (потому что так я оказался здесь):

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

Номер входа останется таким же, если он уже несколько.

Вот вывод x86_64, который GCC дает с помощью -O2 или -Os (9Sep2013 Build - 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

Я использую:

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

Обратите внимание, что оба этих округлых отрицательных значения равны нулю (это означает округлую до положительной бесконечности для всех значений), ни одна из них не полагается на подписанное переполнение (которое является undefined в 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

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

Ответ 13

int noOfMultiples = int((numToRound / multiple)+0.5);
return noOfMultiples*multiple

С++ округляет каждое число вниз, поэтому, если вы добавите 0.5 (если его 1.5 будет 2), но 1.49 будет 1.99, то 1.

EDIT - Извините, я не хотел, чтобы вы хотели округлить, я бы предложил использовать метод ceil() вместо +0.5

Ответ 14

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

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

можно было бы сократить до

int roundUp = roundDown + multiple;
return roundUp;

Ответ 15

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

int RoundUpToNearestMultOfNumber(int val, int num)
{
  assert(0 != num);
  return (floor((val + num) / num) * num);
}

Ответ 16

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

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

Ответ 17

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

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

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

Ответ 18

Для отрицательного numToRound:

Это должно быть действительно легко сделать, но стандартный оператор modulo% не обрабатывает отрицательные числа, как можно было бы ожидать. Например, -14% 12 = -2, а не 10. Первое, что нужно сделать, это получить оператор modulo, который никогда не возвращает отрицательные числа. Тогда roundUp действительно прост.

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

Это то, что я сделал бы:

#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

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

хотя:

  • не будет работать для отрицательных чисел
  • не будет работать, если numRound + multiple overflows

предложит вместо этого использовать беззнаковые целые числа, которые определили поведение переполнения.

Вы получите исключение несколько == 0, но в любом случае это не является четко определенной проблемой.

Ответ 21

с:

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

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

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

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

round_up(19, 3) = 21

Ответ 23

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

Округление до ближайшего кратного, который оказывается степенью 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

Ответ 25

/// 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

Это дает результаты, которые вы ищете для целых положительных чисел:

#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

Я думаю, что это должно помочь вам. Я написал следующую программу на 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

Это работает для меня, но не пытается обрабатывать негативы.

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