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


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

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

EDIT: Спасибо за все ответы. Вот для чего я пошел:

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

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

 return numToRound + multiple - remainder; 
}  
27 135

27 ответов:

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

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

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

    return numToRound + multiple - remainder;
}

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

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

без условия:

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

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

EDIT: версия, которая работает также для отрицательных чисел

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

тесты

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

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

это обобщение проблемы "как узнать, сколько байтов 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
}
int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 return ((numToRound - 1) / multiple + 1) * multiple;  
}

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

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

n - (n % r)

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

(n + r) - (n % r)

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

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

что является более или менее ответом плинтуса, с добавленной отрицательной поддержкой ввода.

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

#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 "или более известную как" круглый потолок", как показано ниже:

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

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

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

во-вторых, вы должны проверить, что numToRound уже не является кратным. В противном случае, когда вы добавляете multiple to 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);
}

раунд в силу двух:

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

// 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 online):

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 идеально соответствует своей строке in собрание:http://goo.gl/DZigfX

каждая из этих инструкций очень быстро, так что функция очень быстро тоже. Поскольку код настолько мал и быстр, это может быть полезно inline функция при ее использовании.


кредит:

Я использую:

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

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

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

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

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

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

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

определенно можно сократить до

int roundUp = roundDown + multiple;
return roundUp;

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

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

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

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

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

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

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

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

это должно быть очень легко сделать, но стандартный оператор по модулю % не обрабатывает отрицательные числа, как можно было бы ожидать. Например -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);
}

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

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

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

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

хотя:

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

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

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

c:

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

и ваш ~/.bashrc следующее:

roundup()
{
  echo $((  ? ((+-1)/)* :  ))
}

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

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

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

round_up(19, 3) = 21

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

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

#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

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

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

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