(Edited)
Округление целых чисел с плавающей точкой является самым простым решением этой проблемы; однако, в зависимости от поставленной задачи это может быть возможно. Например, во встроенных системах решение с плавающей запятой может быть слишком дорогим.
Делать это с использованием целочисленной математики оказывается довольно сложно и немного не интуитивно понятно. Первое опубликованное решение работало хорошо для проблемы, для которой я его использовал, но после характеристики результатов по целому ряду целых оказалось в целом очень плохо. Просматривая несколько книг по битовому и встроенному математике, вы получите немного результатов.
Пара заметок. Во-первых, я проверял только положительные целые числа, моя работа не включает отрицательные числители или знаменатели. Во-вторых, исчерпывающий тест 32-разрядных целых чисел запрещает вычисления, поэтому я начал с 8-разрядных целых чисел, а затем убедился, что получил аналогичные результаты с 16-разрядными целыми числами.
Я начал с двух предложенных мной ранее решений:
#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.) 2-я проблема на самом деле рекурсивная, поскольку для получения правильного ответа необходимо правильно округлить D / 2. 2.) В первом случае вы часто переполняете, а затем переполняете, оба компенсируют друг друга.
Вот график ошибок двух (неправильных) алгоритмов:
Этот график показывает, что первый алгоритм некорректен только для малых знаменателей (0 большие числители лучше, чем вторая версия.
Вот график 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))<br>
else if ((D == 4) && (N > 100))<br>
else if ((D == 5) && (N > 125))<br>
else if ((D == 6) && (N > 150))<br>
else if ((D == 7) && (N > 175))<br>
else if ((D == 8) && (N > 200))<br>
else if ((D == 9) && (N > 225))<br>
else if ((D == 10) && (N > 250))
(верните D / N для них)
Так что в общем случае пуант, в котором конкретный числитель становится плохим, где-то около
N > (MAX_INT - 5) * D/10
Это не точно, но близко. При работе с 16-битными или большими числами ошибка <1%, если вы просто делаете деление C (усечение) для этих случаев. </p>
Для 16-битных чисел со знаком тесты будут
if ((D == 3) && (N >= 9829))<br>
else if ((D == 4) && (N >= 13106))<br>
else if ((D == 5) && (N >= 16382))<br>
else if ((D == 6) && (N >= 19658))<br>
else if ((D == 7) && (N >= 22935))<br>
else if ((D == 8) && (N >= 26211))<br>
else if ((D == 9) && (N >= 29487))<br>
else if ((D == 10) && (N >= 32763))
Конечно, для целых чисел без знака MAX_INT будет заменен на MAX_UINT. Я уверен, что есть точная формула для определения наибольшего N, которая будет работать для определенного D и количества бит, но у меня больше нет времени для решения этой проблемы ...
(мне кажется, что в данный момент мне не хватает этого графика, я отредактирую и добавлю позже.)
Это график 8-битной версии с особыми случаями, отмеченными выше:! [8-битная подпись с особыми случаями для 0 < N <= 10
3
Обратите внимание, что для 8 битов ошибка составляет 10% или менее для всех ошибок в графике, 16 битов составляет <0,1%. </p>