Как округлить результат целочисленного деления?

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

Если у меня есть x элементов, которые я хочу отображать кусками y на странице, сколько страниц потребуется?

20.08.2008 13:27:40
Я что-то упускаю? y / x + 1 отлично работает (если вы знаете, что оператор / всегда округляется).
rikkit 7.08.2012 16:03:00
@rikkit - если y и x равны, y / x + 1 слишком велик.
Ian Nelson 7.08.2012 19:15:40
Для тех, кто только что нашел это, этот ответ на двойной вопрос позволяет избежать ненужного преобразования в удвоение и избежать проблем переполнения в дополнение к предоставлению четкого объяснения.
ZX9 21.12.2016 14:54:11
@IanNelson в более общем случае, если xделится на y, y/x + 1будет слишком большим.
Ohad Schneider 24.08.2017 13:10:31
@ ZX9 Нет, это не помогает избежать проблем переполнения. Это точно такое же решение, как и Ян Нельсон.
user247702 1.12.2017 14:22:04
15 ОТВЕТОВ

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

-4
20.08.2008 13:29:57

Другой альтернативой является использование функции mod () (или «%»). Если есть ненулевой остаток, то увеличиваем целочисленный результат деления.

2
20.08.2008 13:30:43

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

int x = number_of_items;
int y = items_per_page;

// with out library
int pages = x/y + (x % y > 0 ? 1 : 0)

// with library
int pages = (int)Math.Ceiling((double)x / (double)y);
68
20.08.2008 13:39:51
x / y + !! (x% y) избегает ветвления для C-подобных языков. Шансы хорошие, однако, ваш компилятор делает это в любом случае.
Rhys Ulerich 26.01.2010 15:57:26
+1 за не переполнение, как в ответах выше ... хотя преобразование целых чисел в удвоения только для Math.ceiling и затем обратно - плохая идея в чувствительном к производительности коде.
Cogwheel 25.02.2015 01:54:04
@RhysUlerich, который не работает в c # (не может напрямую конвертировать int в bool). Я думаю, что решение rjmunro - единственный способ избежать разветвления.
smead 9.04.2016 01:05:00

Для C # решение состоит в приведении значений к двойному (поскольку Math.Ceiling принимает двойное):

int nPages = (int)Math.Ceiling((double)nItems / (double)nItemsPerPage);

В Java вы должны сделать то же самое с Math.ceil ().

77
29.03.2015 21:24:08
почему этот ответ так далеко вниз, когда оператор явно запрашивает C #!
felickz 1.10.2012 20:46:50
Вам также необходимо преобразовать вывод в intпотому что Math.Ceilingвозвращает a doubleили decimal, в зависимости от типов ввода.
DanM7 17.10.2012 21:28:08
потому что это крайне неэффективно
Zar Shardan 4.04.2013 13:05:18
Это может быть неэффективно, но это чрезвычайно легко понять. Учитывая, что подсчет страниц обычно выполняется один раз для каждого запроса, любая потеря производительности не поддается измерению.
Jared Kells 7.04.2015 06:21:09
Это чуть более читабельно, чем этот "(дивиденд + (делитель - 1)) / делитель;" также медленный и требует математической библиотеки.
rolls 24.04.2017 06:47:48
РЕШЕНИЕ

Нашли элегантное решение:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

Источник: Преобразование чисел, Роланд Бэкхаус, 2001

474
20.08.2008 13:42:08
-1 из-за ошибки переполнения, указанной Брэндоном Дюреттом
finnw 4.05.2011 09:21:33
Мистер Очевидный говорит: не забудьте убедиться, что recordsPerPage не равен нулю
Adam Gent 19.05.2011 11:41:43
Хорошая работа, я не могу поверить, что у C # нет целочисленного потолка.
gosukiwi 29.08.2012 18:32:32
Да, вот я в середине 2017 года наткнулся на этот замечательный ответ после попытки нескольких гораздо более сложных подходов.
Mifo 29.07.2017 23:41:47
Для языков с правильным оператором евклидова деления, таких как Python, был бы еще более простой подход pageCount = -((-records) // recordsPerPage).
supercat 4.07.2018 16:45:29

Целочисленное математическое решение, которое предоставил Ян, хорошо, но страдает от ошибки целочисленного переполнения. Предполагая, что переменные - это все int, решение можно переписать, чтобы использовать longматематику и избежать ошибки:

int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;

Если recordsесть long, ошибка остается. В модульном решении нет ошибки.

18
31.08.2018 06:51:57
Я не думаю, что вы реально решите эту ошибку в представленном сценарии. 2 ^ 31 записей довольно много, чтобы пролистать.
rjmunro 5.02.2009 00:18:28
finnw 4.05.2011 09:20:23
@finnw: AFAICS, на этой странице нет реального примера, просто отчет о том, как кто-то еще обнаружил ошибку в теоретическом сценарии.
rjmunro 29.11.2011 11:44:47
Да, я педантично указывал на ошибку. Многие ошибки могут существовать вечно без каких-либо проблем. Ошибка той же формы существовала в реализации JSK для binarySearch в течение примерно девяти лет, прежде чем кто-то сообщил об этом ( googleresearch.blogspot.com/2006/06/… ). Я предполагаю, что вопрос в том, насколько вы вряд ли столкнетесь с этой ошибкой, почему бы не исправить ее заранее?
Brandon DuRette 29.11.2011 17:59:58
Кроме того, следует отметить, что значение имеет не только количество элементов, которые разбиваются на страницы, но и размер страницы. Итак, если вы создаете библиотеку, и кто-то решает не печатать страницу, передавая 2 ^ 31-1 (Integer.MAX_VALUE) в качестве размера страницы, то ошибка вызвана.
Brandon DuRette 29.11.2011 18:03:42

Переход на плавающую точку и обратно кажется огромной тратой времени на уровне процессора.

Решение Яна Нельсона:

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;
192
2.02.2009 13:26:00
+1, проблема нулевых записей, по-прежнему возвращающих 1 pageCount, на самом деле удобна, так как я все еще хотел бы, чтобы 1 страница, показывающая заполнитель / фальшивую строку «нет записей, соответствующих вашим критериям», помогает избежать любых проблем с «0 счетчиком страниц» в любом случае. контроль пагинации вы используете.
Timothy Walters 17.03.2011 02:38:56
Имейте в виду, что два решения не возвращают один и тот же pageCount для нулевых записей. Эта упрощенная версия вернет 1 pageCount для нулевых записей, тогда как версия Roland Backhouse вернет 0 pageCount. Хорошо, если вы этого хотите, но эти два уравнения не эквивалентны при выполнении целочисленного деления в стиле C # / Java.
Ian Nelson 6.09.2011 07:48:40
крошечное редактирование для ясности для людей, просматривающих его и пропускающих bodmas при переходе к упрощению из решения Нельсона (как я делал в первый раз!), упрощение с помощью скобок ... int pageCount = ((records - 1) / recordsPerPage) + 1;
dave heywood 2.05.2014 08:41:03
Вы должны добавить скобки в упрощенную версию, чтобы она не зависела от определенного порядка операций. то есть, ((records - 1) / recordsPerPage) + 1.
Martin 5.02.2016 17:57:37
@Ian, этот ответ не всегда возвращает 1. Она может возвращать 0 , если ваш recordsPerPage является «1» , и есть 0 записи: -1 / 1 + 1 = 0. Хотя это не очень распространенное явление, важно помнить, разрешаете ли вы пользователям настраивать размер страницы. Так что либо не разрешайте пользователям иметь размер страницы 1, проверяйте размер страницы или и то, и другое (вероятно, предпочтительнее, чтобы избежать неожиданного поведения).
Michael 9.01.2018 23:39:59

Для записей == 0 решение rjmunro дает 1. Правильное решение равно 0. Тем не менее, если вы знаете, что записи> 0 (и я уверен, что мы все предположили recordsPerPage> 0), то решение rjmunro дает правильные результаты и не имеет никаких проблем переполнения.

int pageCount = 0;
if (records > 0)
{
    pageCount = (((records - 1) / recordsPerPage) + 1);
}
// no else required

Все целочисленные математические решения будут более эффективными, чем любое из решений с плавающей запятой.

4
11.02.2009 10:41:28
Этот метод вряд ли будет узким местом в производительности. И если это так, вы также должны учитывать стоимость филиала.
finnw 4.05.2011 13:14:37

Альтернатива для удаления ветвления при тестировании на ноль:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage * (records != 0);

Не уверен, что это будет работать в C #, следует делать в C / C ++.

0
13.08.2010 03:26:08

Общий метод, чей результат вы можете повторить, может представлять интерес:

public static Object[][] chunk(Object[] src, int chunkSize) {

    int overflow = src.length%chunkSize;
    int numChunks = (src.length/chunkSize) + (overflow>0?1:0);
    Object[][] dest = new Object[numChunks][];      
    for (int i=0; i<numChunks; i++) {
        dest[i] = new Object[ (i<numChunks-1 || overflow==0) ? chunkSize : overflow ];
        System.arraycopy(src, i*chunkSize, dest[i], 0, dest[i].length); 
    }
    return dest;
}
-1
28.10.2010 14:09:15
У Guava аналогичный метод ( Lists.partition(List, int)), и по иронии судьбы size()метод получающегося List(по состоянию на r09) страдает от ошибки переполнения, упомянутой в ответе Брэндона Дюрета .
finnw 4.05.2011 13:21:36

Вариант ответа Ника Берарди, который избегает ответвления:

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.

7
23.05.2017 12:02:45

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

int hrs = 0; int mins = 0;

float tm = totalmins;

if ( tm > 60 ) ( hrs = (int) (tm / 60);

mins = (int) (tm - (hrs * 60));

System.out.println("Total time in Hours & Minutes = " + hrs + ":" + mins);
-2
19.03.2012 17:06:05

Следующее должно делать округление лучше, чем вышеупомянутые решения, но за счет производительности (из-за вычисления с плавающей запятой 0,5 * rctDenominator):

uint64_t integerDivide( const uint64_t& rctNumerator, const uint64_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;
}
-2
7.02.2013 15:21:15

Я делаю следующее, обрабатывает любые переполнения:

var totalPages = totalResults.IsDivisble(recordsperpage) ? totalResults/(recordsperpage) : totalResults/(recordsperpage) + 1;

И используйте это расширение, если есть 0 результатов:

public static bool IsDivisble(this int x, int n)
{
           return (x%n) == 0;
}

Кроме того, для текущего номера страницы (не спрашивался, но мог бы быть полезным):

var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1;
1
4.02.2014 09:36:58

При необходимости метода расширения:

    public static int DivideUp(this int dividend, int divisor)
    {
        return (dividend + (divisor - 1)) / divisor;
    }

Никаких проверок здесь (переполнение и DivideByZeroт. Д.), Не стесняйтесь добавлять, если хотите. Кстати, для тех, кто обеспокоен накладными расходами при вызове метода, такие простые функции могут быть встроены компилятором в любом случае, так что я не думаю, что это то, о чем стоит беспокоиться. Приветствия.

PS Вам также может быть полезно знать об этом (он получает остаток):

    int remainder; 
    int result = Math.DivRem(dividend, divisor, out remainder);
3
15.09.2016 19:52:17
Это неверно Например: DivideUp(4, -2)возвращает 0 (должно быть -2). Это верно только для неотрицательных целых чисел, что не ясно из ответа или из интерфейса функции.
Thash 25.04.2017 21:31:24
Thash, почему бы тебе не сделать что-нибудь полезное, например добавить небольшую дополнительную проверку, если число отрицательное, вместо того, чтобы голосовать за мой ответ, и неправильно сделать общее утверждение: «Это неправильно», когда на самом деле это просто край кейс. Я уже дал понять, что вы должны сначала выполнить другие проверки: «Здесь нет проверок (переполнение, DivideByZero и т. Д.), Не стесняйтесь добавлять, если хотите ».
Nicholas Petersen 25.04.2017 23:18:37
В вопросе упоминалось «Я думаю, в частности, о том, как отобразить элементы управления разбиением на страницы », чтобы отрицательные числа были в любом случае за пределами допустимого. Опять же, просто сделайте что-нибудь полезное и предложите дополнительную проверку, если хотите, это человек командного усилия.
Nicholas Petersen 25.04.2017 23:22:24
Я не хотел быть грубым, и мне жаль, если вы так поступите. Вопрос был «Как округлить результат целочисленного деления». Автор упомянул нумерацию страниц, но у других людей могут быть другие потребности. Я думаю, что было бы лучше, если бы ваша функция как-то отразила, что она не работает для отрицательных целых чисел, так как это неясно из интерфейса (например, различное имя или типы аргумента). Для работы с отрицательными целыми числами вы можете, например, взять абсолютное значение делимого и делителя и умножить результат на его знак.
Thash 26.04.2017 07:51:01