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

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

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

Я что-то пропустил? y / x + 1 отлично работает (при условии, что вы знаете, что оператор / всегда округляет в меньшую сторону).

rikkit 07.08.2012 20:03

@rikkit - если y и x равны, y / x + 1 слишком велико.

Ian Nelson 07.08.2012 23:15

Для тех, кто только что обнаружил это, этот ответ на дурацкий вопрос позволяет избежать ненужного преобразования в double и позволяет избежать проблем с переполнением в дополнение к предоставлению четкого объяснения.

ZX9 21.12.2016 17:54

@IanNelson в целом, если x делится на y, y/x + 1 будет слишком большим.

Ohad Schneider 24.08.2017 16:10

@ ZX9 Нет, это не позволяет избежать проблем с переполнением. Это точно такое же решение, которое опубликовал здесь Ян Нельсон.

user247702 01.12.2017 17:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
356
5
214 705
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

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

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

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

x / y + !! (x% y) избегает ветвления для C-подобных языков. Шансы хорошие, но ваш компилятор все равно это делает.

Rhys Ulerich 26.01.2010 18:57

+1 для того, чтобы не переполняться, как в ответах выше ... хотя преобразование int в удвоение только для Math.ceiling, а затем обратно - плохая идея в коде, чувствительном к производительности.

Cogwheel 25.02.2015 04:54

@RhysUlerich, который не работает в C# (не может напрямую преобразовать int в bool). Думаю, решение rjmunro - единственный способ избежать ветвления.

smead 09.04.2016 04:05

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

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

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

почему этот ответ так далеко вниз, когда оператор явно запрашивает C#!

felickz 02.10.2012 00:46

Вам также необходимо преобразовать вывод в int, потому что Math.Ceiling возвращает double или decimal, в зависимости от типов ввода.

DanM7 18.10.2012 01:28

потому что это крайне неэффективно

Zar Shardan 04.04.2013 17:05

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

Jared Kells 07.04.2015 09:21

Едва ли более читабельно, чем это "(делимое + (делитель - 1)) / делитель;" также он медленный и требует математической библиотеки.

rolls 24.04.2017 09:47

@rolls Я думаю, что под «читабельным» он имел в виду, что внимание намного яснее.

Divisadero 05.08.2020 22:03
Ответ принят как подходящий

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

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

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

-1 из-за ошибки переполнения, указанной Брэндон Дюретт

finnw 04.05.2011 13:21

Мистер Очевидный говорит: не забудьте убедиться, что значение recordsPerPage не равно нулю.

Adam Gent 19.05.2011 15:41

Хорошая работа, не могу поверить, что в C# нет целочисленного потолка.

gosukiwi 29.08.2012 22:32

Ага, вот в середине 2017 года я натыкаюсь на этот отличный ответ, попробовав несколько более сложных подходов.

Mifo 30.07.2017 02:41

Для языков с правильным оператором евклидова деления, таких как Python, еще более простым подходом будет pageCount = -((-records) // recordsPerPage).

supercat 04.07.2018 19:45

+1, потому что это также работает с числами без знака (актуально для других языков): «Упрощение» (records - 1) / recordsPerPage + 1 не работает, когда записи == 0.

user2394284 18.03.2019 16:49

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

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

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

Я не думаю, что вы реально столкнетесь с этой ошибкой в ​​представленном сценарии. 2 ^ 31 запись - это довольно много, чтобы пролистать страницу.

rjmunro 05.02.2009 03:18

@rjmunro, реальный пример здесь

finnw 04.05.2011 13:20

@finnw: AFAICS, на этой странице нет реального примера, просто отчет о том, что кто-то другой обнаружил ошибку в теоретическом сценарии.

rjmunro 29.11.2011 15:44

@rjmunro, я имел в виду это: «Я столкнулся с этой странностью, когда работал над программой просмотра таблиц на основе SWT, которая поддерживает разбиение на страницы. Внутренне программа просмотра таблиц использует Lists.partition () для разделения своих входных элементов на страницы фиксированного размера».

finnw 29.11.2011 19:11

Да, я педантично указывал на ошибку. Многие ошибки могут существовать вечно, не вызывая при этом никаких проблем. Ошибка такой же формы существовала в реализации binarySearch в JDK около девяти лет, прежде чем кто-то сообщил о ней (googleresearch.blogspot.com/2006/06/…). Думаю, вопрос в том, почему бы не исправить это заранее, независимо от того, насколько маловероятно, что вы столкнетесь с этой ошибкой?

Brandon DuRette 29.11.2011 21:59

Кроме того, следует отметить, что имеет значение не только количество выгружаемых элементов, но и размер страницы. Итак, если вы создаете библиотеку, и кто-то решает не листать, передав 2 ^ 31-1 (Integer.MAX_VALUE) в качестве размера страницы, то возникает ошибка.

Brandon DuRette 29.11.2011 22:03

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

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

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 страниц» во всем управление разбивкой на страницы, которое вы используете.

Timothy Walters 17.03.2011 05:38

Имейте в виду, что два решения не возвращают одно и то же значение pageCount для нулевых записей. Эта упрощенная версия возвращает 1 pageCount для нулевых записей, тогда как версия Roland Backhouse возвращает 0 pageCount. Хорошо, если вы этого хотите, но эти два уравнения не эквивалентны при выполнении целочисленным делением в стиле C# / Java.

Ian Nelson 06.09.2011 11:48

крошечное редактирование для ясности для людей, просматривающих его и отсутствующих бодм при переходе на упрощение из решения Нельсона (как я сделал в первый раз!), упрощение с помощью скобок ... int pageCount = ((records - 1) / recordsPerPage) +1;

dave heywood 02.05.2014 12:41

Вы должны добавить скобки к упрощенной версии, чтобы она не зависела от определенного порядка операций. то есть ((записи - 1) / recordsPerPage) + 1.

Martin 05.02.2016 20:57

Кроме того, это проще перевести на другие задачи деления; Полезно знать, что есть простой способ разделить потолок целыми числами.

BrainSlugs83 09.07.2016 00:25

@Ian, этот ответ НЕ ВСЕГДА возвращает 1. Он может возвращать 0, если ваш recordsPerPage равен «1» и есть 0 записей: -1 / 1 + 1 = 0. Хотя это не очень распространенное явление, важно помнить о том, разрешаете ли вы пользователям изменять размер страницы. Поэтому либо не разрешайте пользователям иметь размер страницы 1, либо проверьте размер страницы, либо и то, и другое (вероятно, предпочтительнее, чтобы избежать неожиданного поведения).

Michael 10.01.2018 02:39

Однако это не решение проблемы переполнения. С решением Яна вы получаете вдвое больший полезный диапазон, если используете целые числа без знака, а records + recordsPerPage имеет верхнюю границу 2 ^ 32 или 2 ^ 64. С помощью этого решения вы можете использовать только целое число со знаком для records, иначе оно будет переполняться на 0 - 1, поэтому records имеет верхнюю границу 2 ^ 31 или 2 ^ 63.

JimmyMcHoover 27.04.2018 15:17

FWIW, я делал что-то подобное, вычисляя максимальное количество страниц, и если номера страниц начинаются с 0, это становится красивым, чистым ... maxPage = (records - 1) / recordsPerPage. Единственная проблема в том, что у него такая же проблема, когда records == 0 ... если recordsPerPage равно 1, результат будет -1 (чего бы я хотел), но если recordsPerPage> 1, вы получите 0.

KevinVictor 05.03.2020 23:53

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

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

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

Этот метод вряд ли станет узким местом для производительности. А если да, то стоит также учитывать стоимость отделения.

finnw 04.05.2011 17:14

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

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

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

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

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;
}
Гуава has a similar method (Lists.partition(List, int)) and ironically the size() method of the resulting List (as of r09) suffers from the overflow bug mentioned in Ответ Брэндона Дюретта.
finnw 04.05.2011 17:21

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

int q = records / recordsPerPage, r = records % recordsPerPage;
int pageCount = q - (-r >> (Integer.SIZE - 1));

Примечание. (-r >> (Integer.SIZE - 1)) состоит из знакового бита r, повторяемого 32 раза (благодаря знаковому расширению оператора >>). Он оценивается как 0, если r равен нулю или отрицательный, и -1, если r положительный. Таким образом, вычитание его из q приводит к добавлению 1, если records % recordsPerPage > 0.

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

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

Следующее должно выполнять округление лучше, чем приведенные выше решения, но за счет производительности (из-за вычисления с плавающей запятой 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;
}

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

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

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

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

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

var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1;

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

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

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

P.S. вам может быть полезно знать об этом (он получает остаток):

    int remainder; 
    int result = Math.DivRem(dividend, divisor, out remainder);

Это неверно. Например: DivideUp(4, -2) возвращает 0 (должно быть -2). Это правильно только для неотрицательных целых чисел, что не ясно из ответа или из интерфейса функции.

Thash 26.04.2017 00:31

Таш, почему бы тебе не сделать что-нибудь полезное, например, добавить небольшую дополнительную проверку, тогда, если число отрицательное, вместо того, чтобы проголосовать за мой ответ, и неправильно сделать общее заявление: «Это неверно», когда на самом деле это просто край дело. Я уже дал понять, что вы должны сначала выполнить другие проверки: «Здесь нет проверок (переполнение, DivideByZero и т. д.), не стесняйтесь добавлять, если хотите».

Nicholas Petersen 26.04.2017 02:18

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

Nicholas Petersen 26.04.2017 02:22

Я не хотел показаться грубым, и мне очень жаль, если вы так считаете. Возник вопрос: «Как округлить результат целочисленного деления». Автор упомянул разбиение на страницы, но у других людей могут быть другие потребности. Я думаю, было бы лучше, если бы ваша функция каким-то образом отражала, что она не работает для отрицательных целых чисел, поскольку это не ясно из интерфейса (например, другое имя или типы аргументов). Для работы с отрицательными целыми числами вы можете, например, взять абсолютное значение делимого и делителя и умножить результат на его знак.

Thash 26.04.2017 10:51

КАК ОКРУГАТЬ РЕЗУЛЬТАТ ЦЕЛОГО ДЕЛЕНИЯ В C#

Мне было интересно узнать, как лучше всего сделать это на C#, так как мне нужно делать это в цикле почти до 100 тысяч раз. Решения, опубликованные другими пользователями, использующими Математика, получают высокие оценки в ответах, но при тестировании я обнаружил, что они медленные. Джарод Эллиотт предложил лучшую тактику проверки того, производит ли мод что-либо.

int result = (int1 / int2);
if (int1 % int2 != 0) { result++; }

Я запускал это в цикле 1 миллион раз, и это заняло 8 мс. Вот код с использованием Математика:

int result = (int)Math.Ceiling((double)int1 / (double)int2);

В моем тестировании частота составила 14 мс, что значительно дольше.

Другие вопросы по теме