Я решил создать новую тему, так как последняя проблема связана не с сомнительными правилами округления, а с совершенно неверными правилами. Поэтому эта тема должна быть в категории Contribute > Bug. Это не критичная ошибка, но всё же довольно неудобная.
У нас был публичный опрос, в котором проголосовало 106 человек. 69 голосов было подано за «нет» и 37 за «да». Простое деление показывает, что 69/106 составляет примерно 65,09 %. Однако система показывает, что 66 % проголосовали за «нет» и 34 % за «да». Вот скриншот:
Итоговый результат составил 72 голоса за «нет» и 38 за «да», и опрос теперь закрыт. 72/110 составляет примерно 65,45 %, что система также округляет до 66 %, так что вы можете увидеть эту ошибку в действии на Tappara.co.
Я пытался посмотреть код, чтобы найти причину ошибки, но не смог. Я обнаружил, что вызывается функция с именем evenRound, но при поиске её исходного кода я её не нашёл. Это просто предположение, но, возможно, эта функция округляет число до ближайшего чётного целого?
Спасибо за код. Я просто не смог его найти, что лишь подтверждает, насколько я не умею пользоваться GitHub. Код взят со Stack Overflow, где его автор пишет:
Я не уверен, какая точность вам нужна, но я бы просто прибавил единицу к первым n числам, где n — это ceil от общей суммы дробных частей. В данном случае это 3, поэтому я бы прибавил 1 к первым трём элементам, а остальные округлил вниз. Конечно, это не супер точно: некоторые числа могут быть округлены вверх или вниз не там, где нужно, но работает это неплохо и всегда даёт в сумме 100%.
Неудивительно, что при таком грубом методе получаются странные результаты. Я бы предложил быть чуть более изощрёнными. Этот пост в той же теме имеет 72 апвоута, в то время как пост, откуда взят код, — всего 1.
Существует множество способов сделать это, если вас не волнует зависимость от исходных десятичных данных.
Первый и, возможно, самый популярный метод — это метод наибольших остатков.
Который по сути выглядит так:
Округляем всё вниз
Вычисляем разницу между суммой и 100
Распределяем эту разницу, прибавляя по 1 к элементам в порядке убывания их дробных частей
…
Я думаю, этот метод был бы лучшим выбором. Он всё ещё очень прост и даёт гораздо лучшие результаты. Текущему коду нужно добавить ещё несколько строк, чтобы указать функции, какие числа следует округлять вверх. Однако есть ещё один нюанс. Из этого поста в той же теме:
Хорошо проголосованный ответ Варуна Вохры минимизирует сумму абсолютных ошибок, и его очень просто реализовать. Однако есть граничные случаи, которые он не обрабатывает — каков должен быть результат округления 24,25, 23,25, 27,25, 25,25? Одно из этих чисел нужно округлить вверх вместо вниз.
В посте предлагаются различные методы выбора того, какое число округлять вверх, а какое вниз, но полностью избежать произвольного выбора невозможно. Если вы стремитесь к совершенству, можете прочитать этот пост дальше, но в этих редких особых случаях меня бы устроил и этот вариант.
Вы, вероятно, просто произвольно выберете первый или последний элемент в списке.
Надеюсь, это поможет. Мои навыки, вероятно, позволили бы мне внести улучшения самостоятельно, если бы я сначала изучил базовый синтаксис. Сейчас я этим заниматься не буду, но, возможно, когда-нибудь, если кто-то не займётся этим раньше.
P.S. Я немного улучшил заголовок, заменив слово “broken” на “imprecise”. Эту тему также следует перенести в Contribute > Feature или объединить с предыдущей темой.
It was like classic exercises for beginning programmers. Here is a way to do it, but someone who has more experience and isn’t totally new to JavaScript might find some shortcuts.