Округление целочисленного деления (вместо усечения)
мне было любопытно знать, как я могу округлить число до ближайшего десятая целое число. Например, если бы я имел:
int a = 59 / 4;
который будет 14.75 вычисляется с плавающей запятой; как я могу сохранить число как 15 в "a"?
18 ответов:
int a = 59.0f / 4.0f + 0.5f;это работает только при назначении int, поскольку он отбрасывает что-либо после '.-
Edit: Это решение будет работать только в самых простых случаях. Более надежным решением было бы:
unsigned int round_closest(unsigned int dividend, unsigned int divisor) { return (dividend + (divisor / 2)) / divisor; }
стандартная идиома для округления целых чисел:
int a = (59 + (4 - 1)) / 4;вы добавляете делитель минус один к дивиденду.
код, который работает для любого знака в dividend и divisor:
int divRoundClosest(const int n, const int d) { return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d); }Если вы предпочитаете макрос:
#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))макрос ядра linux DIV_ROUND_CLOSEST не работает для отрицательных делителей!
вместо этого, вы должны использовать что-то вроде этого:
int a = (59 - 1)/ 4 + 1;Я предполагаю, что вы действительно пытаются сделать что-то более общее:
int divide(x, y) { int a = (x -1)/y +1; return a; }x + (y-1) имеет потенциал переполнения, давая неверный результат; в то время как x - 1 будет только underflow, если x = min_int...
(редактировать) Округление чисел с плавающей точкой является самым простым решением этой проблемы; однако, в зависимости от задания это может быть возможно. Например, во встроенных системах решение с плавающей запятой может быть слишком дорогостоящим.
делать это с помощью целочисленной математики оказывается довольно сложно и немного неинтуитивно. Первое опубликованное решение работало нормально для проблемы, для которой я его использовал, но после характеристики результатов в диапазоне целых чисел оказалось, что очень плохо вообще. Просмотр нескольких книг по битовому скручиванию и встроенной математике возвращает мало результатов. Несколько заметок. Во-первых, я тестировал только положительные целые числа, моя работа не включает отрицательные числители или знаменатели. Во-вторых, исчерпывающий тест 32-битных целых чисел является вычислительным запретительным, поэтому я начал с 8-битных целых чисел, а затем убедился, что получил аналогичные результаты с 16-битными целыми числами.
Я начал с 2-х решений, которые я имел ранее предложил:
#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)
#define DIVIDE_WITH_ROUND(N, D) (N == 0) ? 0:(N - D/2)/D + 1;Я думал, что первая версия будет переполняться большими числами, а вторая-с небольшими числами. Я не принял во внимание 2 вещи. 1.) вторая проблема на самом деле рекурсивна, так как для получения правильного ответа вы должны правильно округлить D/2. 2.) В первом случае вы часто переполняетесь, а затем недоливаете, два отменяя друг друга. Вот график ошибок из двух (неверно) алгоритмы:
этот график показывает, что первый алгоритм неверен только для малых знаменателей (0 большой числители лучше, чем 2-й вариант.
вот сюжет 2-го алгоритма:
как и ожидалось, он терпит неудачу для небольших числителей, но также терпит неудачу для более крупных числителей, чем 1-я версия.
очевидно, что это лучшая отправная точка для правильного версия:
#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)если ваши знаменатели > 10, то это будет работать правильно.
особый случай необходим для D == 1, просто верните N. Особый случай необходим для D== 2, = N / 2 + (N & 1) / / округление, если нечетное.
D > = 3 также имеет проблемы, как только N становится достаточно большим. Оказывается, что большие знаменатели имеют проблемы только с большими числителями. Для 8-битного числа со знаком проблемными точками являются
if (D == 3) && (N > 75))
else if ((D == 4) && (N > 100))
else if ((D == 5) && (N > 125))
else if ((D == 6) && (N > 150))
else if ((D == 7) && (N > 175))
else if ((D == 8) && (N > 200))
else if ((D == 9) && (N > 225))
else if ((D == 10) && (N > 250))(возврат D / N для них)
так что в целом точка, где конкретный числитель становится плохим, находится где-то вокруг
N > (MAX_INT - 5) * D/10это не точно, но близко. При работе с 16-битными или большими числами Ошибка
для 16-битных подписанных чисел тесты будут
if ((D == 3) && (N >= 9829))
else if ((D == 4) && (N >= 13106))
else if ((D == 5) && (N >= 16382))
else if ((D == 6) && (N >= 19658))
else if ((D == 7) && (N >= 22935))
else if ((D == 8) && (N >= 26211))
else if ((D == 9) && (N >= 29487))
else if ((D == 10) && (N >= 32763))конечно, для целых чисел без знака MAX_INT будет заменен на MAX_UINT. Я уверен, что есть точная формула для определения наибольшего N, которая будет работать для определенного D и количества бит, но у меня больше нет времени для работы над этой проблемой...
(кажется, мне не хватает этого графика на данный момент, я буду редактировать и добавлять позже.) Это график 8-битной версии с особыми случаями, отмеченными выше:
