Хорошо - я почти смущен, когда публикую это здесь (и я буду удалять, если кто-то проголосует за закрытие), поскольку это кажется основным вопросом.
Это правильный способ округлить до кратного числа в 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
.Ответы:
Это работает для положительных чисел, а не для отрицательных. Используется только целочисленная математика.
Редактировать: вот версия, которая работает с отрицательными числами, если под «вверх» вы подразумеваете результат, который всегда> = ввод.
источник
if(number<0){ multiple = multiple*(-1); }
в начале, чтобы округлить отрицательные числа в правильном направленииif(number<0) multiple = -multiple
это легче.if (remainder == 0)
Тест должен позаботиться о таком случае. Это работает для меня: ideone.com/Waol7BБез условий:
Это работает как округление от нуля для отрицательных чисел
РЕДАКТИРОВАТЬ: версия, которая работает также для отрицательных чисел
тесты
Если
multiple
это степень 2 (быстрее в ~ 3,7 раза http://quick-bench.com/sgPEZV9AUDqtx2uujRSa3-eTE80 )тесты
источник
& ~(x - 1)
это то же самое, что и& -x
для арифметики дополнения до двух.Это работает, когда фактор всегда будет положительным:
Редактировать: это возвращается
round_up(0,100)=100
. Пожалуйста, смотрите комментарий Павла ниже для решения, которое возвращаетсяround_up(0,100)=0
.источник
num + factor - 1 - (num + factor - 1) % factor
?num - 1 - (num - 1) % factor + factor
выполняет те же вычисления без риска переполнения целых чисел.Это обобщение проблемы «как узнать, сколько байтов будет занимать n бит?» (A: (n бит + 7) / 8).
источник
(x = roundTo - 1; return (n+x)&~roundTo;)
как в моем ответе0xFFF...000
, а не0xFFF7FFF
или чем-то, поэтому вы хотите либо отрицание дополнения 2 (-
: минус) со степенью 2, либо переворот на единицу меньше степени 2 (обратное дополнение дополнения~
: tilde не минус). Так(n+x) & ~x
или(n-roundTo+1) & -roundTo
.И не надо возиться с условиями
источник
Для тех, кто ищет короткий и сладкий ответ. Это то, что я использовал. Нет учета негативов.
Это вернет предыдущий фактор.
Вернется следующий. Надеюсь, это кому-нибудь поможет. :)
источник
Это работает для любого числа с плавающей точкой или базы (например, вы можете округлить -4 до ближайшего 6,75). По сути это преобразование в фиксированную точку, округление там, а затем обратное преобразование. Он обрабатывает негативы, округляя AWAY от 0. Он также обрабатывает отрицательный раунд до значения, по сути превращая функцию в roundDown.
Специфичная для int версия выглядит так:
Что является более или менее ответом плинтуса с добавленной отрицательной поддержкой ввода.
источник
double round(double value, double multiple) { double sign = value; multiple = std::copysign(multiple, 1.0); value = std::copysign(value, 1.0); return std::copysign(multiple * std::ceil(value / multiple), sign); }
Или поменять потолок на раунд, чтобы получить округление.Это современный подход c ++, использующий шаблонную функцию, которая работает для float, double, long, int и short (но не для long long и long double из-за используемых значений double).
Но вы можете легко добавить поддержку
long long
иlong double
специализацию шаблонов, как показано ниже:Чтобы создать функции для округления, используйте
std::ceil
и всегда округляйтеstd::floor
. Мой пример сверху округляет с помощьюstd::round
.Создайте шаблонную функцию «округление вверх» или более известную как «круглый потолок», как показано ниже:
Создайте шаблонную функцию «круглый вниз» или более известную как «круглый пол», как показано ниже:
источник
long long
иlong double
. То же самое должно быть сделано для двух других функций, очевидно.Во-первых, ваше условие ошибки (множественное == 0) должно иметь возвращаемое значение. Какой? Я не знаю. Может быть, вы хотите выбросить исключение, это зависит от вас. Но возвращать ничего не опасно.
Во-вторых, вы должны проверить, что numToRound уже не кратен. В противном случае, когда вы добавляете
multiple
вroundDown
, вы получите неправильный ответ.В-третьих, ваши высказывания неверны. Вы бросили
numToRound
приводите к целому числу, но это уже целое число. Вам нужно привести к удвоению до деления и обратно к int после умножения.Наконец, что вы хотите для отрицательных чисел? Округление «вверх» может означать округление до нуля (округление в том же направлении, что и положительные числа) или от нуля («большее» отрицательное число). Или, может быть, вам все равно.
Вот версия с первыми тремя исправлениями, но я не имею дело с отрицательной проблемой:
источник
int / int
что вернет int, а это не то, что мы хотели.Округление до степени двойки:
На всякий случай, если кому-то нужно решение для положительных чисел, округленных до ближайшего кратного степени, равной двум (потому что именно так я здесь и оказался):
Введенный номер останется прежним, если он уже кратен.
Вот вывод x86_64, который GCC выдает с помощью
-O2
или-Os
(9Sep2013 Build - Godbolt GCC онлайн):Каждая строка кода C идеально соответствует своей строке в сборке: http://goo.gl/DZigfX
Каждая из этих инструкций очень быстрая , поэтому функция тоже очень быстрая. Поскольку код такой маленький и быстрый, он может быть полезен
inline
функции при его использовании.Кредит:
источник
Я использую:
и для полномочий двух:
Обратите внимание, что оба из этих округленных отрицательных значений до нуля (что означает округление до положительной бесконечности для всех значений), ни одно из них не полагается на переполнение со знаком (которое не определено в C / C ++).
Это дает:
источник
n_Align_Up_POT
тех пор, как увидел его в классе Delphi TList. У него есть свои ограничения, такие как выравнивание (кратное), являющееся степенью 2, но это редко является проблемой, потому что я в основном использую его для получения / проверки правильного выравнивания для SMID. Это потрясающе, и, кажется, мало кто знает об этом.Вероятно, безопаснее привести к float и использовать ceil () - если только вы не знаете, что деление int даст правильный результат.
источник
C ++ округляет каждое число вниз, поэтому, если вы добавите 0,5 (если его 1,5, то это будет 2), но 1,49 будет 1,99, следовательно, 1.
РЕДАКТИРОВАТЬ - Извините, не видел, что вы хотели округлить, я бы предложил использовать метод ceil () вместо +0,5
источник
ну с одной стороны, так как я не очень понимаю, что вы хотите сделать, линии
определенно может быть сокращено до
источник
может быть, это может помочь:
источник
Чтобы всегда округлять
alwaysRoundUp (1, 10) -> 10
alwaysRoundUp (5, 10) -> 10
alwaysRoundUp (10, 10) -> 10
Чтобы всегда округлять
alwaysRoundDown (1, 10) -> 0
alwaysRoundDown (5, 10) -> 0
alwaysRoundDown (10, 10) -> 10
Округлить нормальный путь
normalRound (1, 10) -> 0
normalRound (5, 10) -> 10
normalRound (10, 10) -> 10
источник
Округление до ближайшего кратного, который оказывается степенью 2
Это может быть полезно при распределении по кеш-линиям, где желаемый прирост округления является степенью двойки, но результирующее значение должно быть только кратным ему. На
gcc
теле этой функции генерируется 8 инструкций по сборке без деления или ветвей.источник
Я нашел алгоритм, который несколько похож на тот, который выложен выше:
int [(| x | + n-1) / n] * [(nx) / | x |], где x - это значение, введенное пользователем, а n - это кратное число.
Работает для всех значений x, где x - целое число (положительное или отрицательное, включая ноль). Я написал это специально для программы на C ++, но в принципе это можно реализовать на любом языке.
источник
Для отрицательного numToRound:
Это должно быть действительно легко, но стандартный оператор по модулю% не обрабатывает отрицательные числа, как можно было бы ожидать. Например, -14% 12 = -2, а не 10. Первое, что нужно сделать, это получить оператор по модулю, который никогда не возвращает отрицательные числа. Тогда раунд действительно прост.
источник
Вот что я бы сделал:
Код может быть неоптимальным, но я предпочитаю чистый код, чем сухую производительность.
источник
int
кfloat
легко теряет точность и делает для неправильных ответов.хотя:
предложил бы вместо этого использовать целые числа без знака, что определило поведение переполнения.
Вы получите исключение, кратное == 0, но в любом случае это не является четко определенной проблемой.
источник
с:
и для вашего ~ / .bashrc:
источник
Я использую комбинацию модулей, чтобы аннулировать добавление остатка, если
x
он уже кратен:Мы находим обратное значение остатка, а затем модуль, который снова делим с делителем, чтобы обнулить его, если это сам делитель, и затем прибавляем
x
.источник
Вот мое решение, основанное на предложении ОП, и примеры, приведенные всеми остальными. Так как большинство всех искало его для обработки отрицательных чисел, это решение делает именно это, без использования каких-либо специальных функций, например, abs и т.п.
Избегая модуля и используя вместо этого деление, отрицательное число является естественным результатом, хотя оно округляется в меньшую сторону. После вычисления округленной версии выполняется математическое вычисление для округления в отрицательную или положительную сторону.
Также обратите внимание, что никакие специальные функции не используются для вычисления чего-либо, поэтому здесь есть небольшое повышение скорости.
источник
RoundUp(INT_MIN, -1)
какn / multiple
этоint
переполнение.Я думаю, что это должно помочь вам. Я написал следующую программу на C.
источник
источник
Это дает результаты, которые вы ищете для положительных целых чисел:
А вот и выводы:
источник
Я думаю, что это работает:
источник
Это работает для меня, но не пытался обрабатывать негативы
источник
Вот супер простое решение, чтобы показать концепцию элегантности. Это в основном для привязки сетки.
(псевдокод)
источник