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