Обратите внимание, что ваша формула неверна, так как в ней пропущено +0.5
, необходимое для округления до ближайшего.
Итак, я продолжу предполагать, что эта исправленная формула:
(unsigned long long)( ((double)partialSize)/((double)totalSize) * 100.0 + 0.5);
Как я уже упоминал в комментариях, простой метод, хотя и простой, не гарантирует правильного округления результатов. Так что ваша интуиция права в том, что она не пуленепробиваема.
В подавляющем большинстве случаев оно все равно будет правильным, но будет небольшой набор пограничных случаев, где оно не будет правильно округлено. Независимо от того, зависит ли это от вас. Но простого метода обычно достаточно для большинства целей.
Почему может произойти сбой:
Есть 4 уровня округления. (исправлено из 2, которые я упоминал в комментариях)
- 64-битные разряды -> 53-битные
- Дивизион
- Умножить на 100.
- Окончательный состав.
Всякий раз, когда у вас есть несколько источников округления, вы страдаете от обычных источников ошибок с плавающей запятой.
Примеры счетчиков:
Хотя это редко, я приведу несколько примеров, когда прямая формула даст неверно округленный результат:
850536266682995018 / 3335436339933313800 // Correct: 25% Formula: 26%
3552239702028979196 / 10006309019799941400 // Correct: 35% Formula: 36%
1680850982666015624 / 2384185791015625000 // Correct: 70% Formula: 71%
Решение:
Я не могу придумать чистого 100% пуленепробиваемого решения, кроме как использовать произвольную точность арифметики .
Но, в конце концов, вам действительно нужно, чтобы оно всегда было идеально округленным?
РЕДАКТИРОВАТЬ:
Для небольших чисел, вот очень простое решение, которое округляется до 0.5
:
return (x * 100 + y/2) / y;
Это будет работать до тех пор, пока x * 100 + y/2
не переполнится.
@ Ответ Даниэля Фишера предлагает более полное решение для других способов округления. Хотя это не должно быть слишком сложно, чтобы изменить это, чтобы округлить до.