Я что-то упускаю? y / x + 1 прекрасно работает (если вы знаете, что оператор / всегда округляется).
Риккит
51
@rikkit - если y и x равны, y / x + 1 слишком велик.
Ян Нельсон
1
Для тех, кто только что нашел это, этот ответ на двойной вопрос позволяет избежать ненужного преобразования в удвоение и избежать проблем с переполнением в дополнение к четкому объяснению.
ZX9
2
@IanNelson в общем случае, если xделится на y, y/x + 1будет слишком большим.
Охад Шнайдер
1
@ ZX9 Нет, это не помогает избежать проблем переполнения. Это точно такое же решение, как и Ян Нельсон.
user247702
Ответы:
478
Нашли элегантное решение:
int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
-1 из-за ошибки переполнения, на которую указывает Брэндон Дюретт
finnw
30
Мистер Очевидный говорит: не забудьте убедиться, что recordsPerPage не равен нулю
Адам Гент
7
Хорошая работа, я не могу поверить, что у C # нет целочисленного потолка.
Госукиви
2
Да, вот я в середине 2017 года наткнулся на этот замечательный ответ после попытки нескольких гораздо более сложных подходов.
Mifo
1
Для языков с правильным оператором евклидова деления, таких как Python, был бы еще более простой подход pageCount = -((-records) // recordsPerPage).
суперкат
194
Преобразование в плавающую точку и обратно кажется огромной тратой времени на уровне процессора.
Решение Яна Нельсона:
int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
Можно упростить до:
int pageCount =(records -1)/ recordsPerPage +1;
AFAICS, здесь нет ошибки переполнения, на которую указал Брэндон Дуретт, и поскольку она использует ее только один раз, вам не нужно специально хранить recordsPerPage, если она исходит из дорогой функции для извлечения значения из файла конфигурации или что-то.
Т.е. это может быть неэффективно, если config.fetch_value использует поиск в базе данных или что-то в этом роде:
int pageCount =(records + config.fetch_value('records per page')-1)/ config.fetch_value('records per page');
Это создает переменную, которая вам на самом деле не нужна, которая, вероятно, имеет (незначительные) последствия для памяти и просто слишком много печатает:
int recordsPerPage = config.fetch_value('records per page')int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
Это все одна строка, и данные извлекаются только один раз:
int pageCount =(records -1)/ config.fetch_value('records per page')+1;
+1, проблема нулевых записей, по-прежнему возвращающих 1 pageCount, на самом деле удобна, так как я все еще хотел бы, чтобы 1 страница, показывающая заполнитель / фальшивую строку «нет записей, соответствующих вашим критериям», помогает избежать любых проблем с «0 счетчиком страниц» в любом случае. контроль пагинации вы используете.
Тимоти Уолтерс
27
Имейте в виду, что два решения не возвращают один и тот же pageCount для нулевых записей. Эта упрощенная версия вернет 1 pageCount для нулевых записей, тогда как версия Roland Backhouse вернет 0 pageCount. Хорошо, если вы этого хотите, но эти два уравнения не эквивалентны при выполнении целочисленного деления в стиле C # / Java.
Ян Нельсон
10
крошечное редактирование для ясности для людей, просматривающих его и пропускающих bodmas при переходе к упрощению из решения Нельсона (как я делал в первый раз!), упрощение с помощью скобок: ... int pageCount = ((records - 1) / recordsPerPage) + 1;
Дейв Хейвуд
1
Вы должны добавить скобки в упрощенную версию, чтобы она не зависела от определенного порядка операций. то есть, ((records - 1) / recordsPerPage) + 1.
Martin
1
@Ian, этот ответ не всегда возвращает 1. Она может возвращать 0 , если ваш recordsPerPage является «1» , и есть 0 записи: -1 / 1 + 1 = 0. Хотя это не очень распространенное явление, важно помнить, разрешаете ли вы пользователям настраивать размер страницы. Так что либо не разрешайте пользователям иметь размер страницы 1, проверяйте размер страницы или и то, и другое (вероятно, предпочтительнее, чтобы избежать неожиданного поведения).
Майкл
81
Для C # решение состоит в приведении значений к двойному (поскольку Math.Ceiling принимает двойное):
int nPages =(int)Math.Ceiling((double)nItems /(double)nItemsPerPage);
В Java вы должны сделать то же самое с Math.ceil ().
почему этот ответ так далеко вниз, когда оператор явно запрашивает C #!
felickz
2
Вам также необходимо преобразовать вывод в intпотому что Math.Ceilingвозвращает a doubleили decimal, в зависимости от типов ввода.
DanM7
15
потому что это крайне неэффективно
Zar Shardan
6
Это может быть неэффективно, но это чрезвычайно легко понять. Учитывая, что подсчет страниц обычно выполняется один раз за запрос, любая потеря производительности не поддается измерению.
Джаред Келлс
1
Это чуть более читабельно, чем этот "(дивиденд + (делитель - 1)) / делитель;" также медленный и требует математической библиотеки.
валки
68
Это должно дать вам то, что вы хотите. Вы определенно захотите, чтобы x элементов делилось на y элементов на странице, проблема в том, что появляются неровные числа, поэтому, если есть частичная страница, мы также хотим добавить одну страницу.
int x = number_of_items;int y = items_per_page;// with out libraryint pages = x/y +(x % y >0?1:0)// with libraryint pages =(int)Math.Ceiling((double)x /(double)y);
x / y + !! (x% y) избегает ветвления для C-подобных языков. Шансы хорошие, однако, ваш компилятор делает это в любом случае.
Рис Улерих
2
+1 за не переполнение, как в ответах выше ... хотя преобразование целых чисел в удвоения только для Math.ceiling и затем обратно - плохая идея в чувствительном к производительности коде.
зубчатое
3
@RhysUlerich, который не работает в c # (не может напрямую конвертировать int в bool). Я думаю, что решение rjmunro - единственный способ избежать ветвления.
Смеад
18
Целочисленное математическое решение, которое предоставил Ян, хорошо, но страдает от ошибки целочисленного переполнения. Предполагая, что переменные - это все int, решение можно переписать, чтобы использовать longматематику и избежать ошибки:
int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;
Если recordsесть long, ошибка остается. В модульном решении нет ошибки.
@finnw: AFAICS, на этой странице нет реального примера, просто отчет о том, как кто-то обнаружил ошибку в теоретическом сценарии.
rjmunro
5
Да, я педантично указывал на ошибку. Многие ошибки могут существовать вечно без каких-либо проблем. Ошибка той же формы существовала в реализации JSK для binarySearch в течение примерно девяти лет, прежде чем кто-то сообщил об этом ( googleresearch.blogspot.com/2006/06/… ). Я предполагаю, что вопрос в том, насколько вы вряд ли столкнетесь с этой ошибкой, почему бы не исправить ее заранее?
Брэндон Дюретт
4
Кроме того, следует отметить, что значение имеет не только количество элементов, которые разбиваются на страницы, но и размер страницы. Итак, если вы создаете библиотеку, и кто-то решает не публиковать страницы, передавая 2 ^ 31-1 (Integer.MAX_VALUE) в качестве размера страницы, тогда возникает ошибка.
int q = records / recordsPerPage, r = records % recordsPerPage;int pageCount = q -(-r >>(Integer.SIZE -1));
Примечание: (-r >> (Integer.SIZE - 1))состоит из знакового бита r, повторяемого 32 раза (благодаря расширению знака >>оператора.). Значение rравно 0, если равно нулю или отрицательно, и -1, если rположительно. Таким образом, вычитание его из qимеет эффект добавления 1, если records % recordsPerPage > 0.
Для записей == 0 решение rjmunro дает 1. Правильное решение равно 0. Тем не менее, если вы знаете, что записи> 0 (и я уверен, что мы все предположили recordsPerPage> 0), то решение rjmunro дает правильные результаты и не имеет никаких проблем переполнения.
int pageCount =0;if(records >0){
pageCount =(((records -1)/ recordsPerPage)+1);}// no else required
Все целочисленные математические решения будут более эффективными, чем любое из решений с плавающей запятой.
Никаких проверок здесь (переполнение и DivideByZeroт. Д.), Не стесняйтесь добавлять, если хотите. Между прочим, для тех, кто обеспокоен накладными расходами при вызове метода, такие простые функции могут быть встроены компилятором в любом случае, так что я не думаю, что это то, что должно беспокоить. Приветствия.
PS Вам также может быть полезно знать об этом (он получает остаток):
int remainder;int result =Math.DivRem(dividend, divisor,out remainder);
Это неверно Например: DivideUp(4, -2)возвращает 0 (должно быть -2). Это верно только для неотрицательных целых чисел, что не ясно из ответа или из интерфейса функции.
Тхач
6
Thash, почему бы тебе не сделать что-то полезное, например добавить небольшую дополнительную проверку, если число отрицательное, вместо того, чтобы голосовать за мой ответ, и неправильно сделать общее утверждение: «Это неправильно», когда на самом деле это просто край кейс. Я уже дал понять, что вы должны сначала сделать другие проверки: «Здесь нет проверок (переполнение, DivideByZero и т. Д.), Не стесняйтесь добавлять, если хотите ».
Николас Петерсен
1
В вопросе упоминалось «Я думаю, в частности, о том, как отобразить элементы управления разбиением на страницы », чтобы отрицательные числа были в любом случае за пределами допустимого. Опять же, просто сделайте что-нибудь полезное и предложите дополнительную проверку, если хотите, это человек командного усилия.
Николас Петерсен
Я не хотел быть грубым, и мне жаль, если вы так поступите. Вопрос был «Как округлить результат целочисленного деления». Автор упомянул о нумерации страниц, но у других людей могут быть другие потребности. Я думаю, что было бы лучше, если бы ваша функция как-то отразила, что она не работает для отрицательных целых чисел, поскольку она не понятна из интерфейса (например, различного имени или типа аргумента). Для работы с отрицательными целыми числами вы можете, например, взять абсолютное значение дивиденда и делителя и умножить результат на его знак.
Thash
2
Другой альтернативой является использование функции mod () (или «%»). Если есть ненулевой остаток, то увеличиваем целочисленный результат деления.
Следующее должно делать округление лучше, чем вышеупомянутые решения, но за счет производительности (из-за вычисления с плавающей запятой 0,5 * rctDenominator):
uint64_t integerDivide(constuint64_t& rctNumerator,constuint64_t& rctDenominator ){// Ensure .5 upwards is rounded up (otherwise integer division just truncates - ie gives no remainder)return(rctDenominator ==0)?0:(rctNumerator +(int)(0.5*rctDenominator))/ rctDenominator;}
x
делится наy
,y/x + 1
будет слишком большим.Ответы:
Нашли элегантное решение:
Источник: Преобразование чисел, Роланд Бэкхаус, 2001
источник
pageCount = -((-records) // recordsPerPage)
.Преобразование в плавающую точку и обратно кажется огромной тратой времени на уровне процессора.
Решение Яна Нельсона:
Можно упростить до:
AFAICS, здесь нет ошибки переполнения, на которую указал Брэндон Дуретт, и поскольку она использует ее только один раз, вам не нужно специально хранить recordsPerPage, если она исходит из дорогой функции для извлечения значения из файла конфигурации или что-то.
Т.е. это может быть неэффективно, если config.fetch_value использует поиск в базе данных или что-то в этом роде:
Это создает переменную, которая вам на самом деле не нужна, которая, вероятно, имеет (незначительные) последствия для памяти и просто слишком много печатает:
Это все одна строка, и данные извлекаются только один раз:
источник
-1 / 1 + 1 = 0
. Хотя это не очень распространенное явление, важно помнить, разрешаете ли вы пользователям настраивать размер страницы. Так что либо не разрешайте пользователям иметь размер страницы 1, проверяйте размер страницы или и то, и другое (вероятно, предпочтительнее, чтобы избежать неожиданного поведения).Для C # решение состоит в приведении значений к двойному (поскольку Math.Ceiling принимает двойное):
В Java вы должны сделать то же самое с Math.ceil ().
источник
int
потому чтоMath.Ceiling
возвращает adouble
илиdecimal
, в зависимости от типов ввода.Это должно дать вам то, что вы хотите. Вы определенно захотите, чтобы x элементов делилось на y элементов на странице, проблема в том, что появляются неровные числа, поэтому, если есть частичная страница, мы также хотим добавить одну страницу.
источник
Целочисленное математическое решение, которое предоставил Ян, хорошо, но страдает от ошибки целочисленного переполнения. Предполагая, что переменные - это все
int
, решение можно переписать, чтобы использоватьlong
математику и избежать ошибки:int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;
Если
records
естьlong
, ошибка остается. В модульном решении нет ошибки.источник
Вариант ответа Ника Берарди, который избегает ответвления:
Примечание:
(-r >> (Integer.SIZE - 1))
состоит из знакового битаr
, повторяемого 32 раза (благодаря расширению знака>>
оператора.). Значениеr
равно 0, если равно нулю или отрицательно, и -1, еслиr
положительно. Таким образом, вычитание его изq
имеет эффект добавления 1, еслиrecords % recordsPerPage > 0
.источник
Для записей == 0 решение rjmunro дает 1. Правильное решение равно 0. Тем не менее, если вы знаете, что записи> 0 (и я уверен, что мы все предположили recordsPerPage> 0), то решение rjmunro дает правильные результаты и не имеет никаких проблем переполнения.
Все целочисленные математические решения будут более эффективными, чем любое из решений с плавающей запятой.
источник
При необходимости метода расширения:
Никаких проверок здесь (переполнение и
DivideByZero
т. Д.), Не стесняйтесь добавлять, если хотите. Между прочим, для тех, кто обеспокоен накладными расходами при вызове метода, такие простые функции могут быть встроены компилятором в любом случае, так что я не думаю, что это то, что должно беспокоить. Приветствия.PS Вам также может быть полезно знать об этом (он получает остаток):
источник
DivideUp(4, -2)
возвращает 0 (должно быть -2). Это верно только для неотрицательных целых чисел, что не ясно из ответа или из интерфейса функции.Другой альтернативой является использование функции mod () (или «%»). Если есть ненулевой остаток, то увеличиваем целочисленный результат деления.
источник
Я делаю следующее, обрабатывает любые переполнения:
И используйте это расширение, если есть 0 результатов:
Кроме того, для текущего номера страницы (не спрашивался, но мог бы быть полезным):
источник
Альтернатива для удаления ветвления при тестировании на ноль:
Не уверен, что это будет работать в C #, следует делать в C / C ++.
источник
Общий метод, чей результат вы можете перебрать, может представлять интерес:
источник
Lists.partition(List, int)
), и по иронии судьбыsize()
метод, полученный в результатеList
(по состоянию наr09
), страдает от ошибки переполнения, упомянутой в ответе Брэндона Дюретта .У меня была похожая потребность, когда мне нужно было конвертировать минуты в часы и минуты. То, что я использовал, было:
источник
Следующее должно делать округление лучше, чем вышеупомянутые решения, но за счет производительности (из-за вычисления с плавающей запятой 0,5 * rctDenominator):
источник
Вы захотите выполнить деление с плавающей запятой, а затем использовать функцию потолка, чтобы округлить значение до следующего целого числа.
источник