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

StackOverflow https://stackoverflow.com/questions/17944

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

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

Источник: Преобразование чисел, Roland Backhouse, 2001

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

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

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

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;

Для C # решение состоит в том, чтобы преобразовать значения в double (как математически.Ceiling принимает значение double):

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

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

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

Решение по целочисленной математике, предоставленное Иэном, хорошее, но страдает от ошибки переполнения целых чисел.Предполагая, что все переменные являются int, решение могло бы быть переписано для использования long математика и предотвращение ошибки:

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

Если records является long, ошибка остается.Модульное решение не содержит этой ошибки.

Вариант Ответ Ника Берарди это позволяет избежать ответвления:

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.

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

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

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

Нуждающийся в методе расширения:

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

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

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

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;

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

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

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

для C # используйте Math.Функция Ceiling:

var pageCount= Math.Ceiling((double)myList.Count() / recordsPerPage);

и Java использует математику.Функция Ceil:

int n = (int) Math.ceil((double)myList.size() / recordsPerPage));

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top