Округление процентов в опросах неточное

Продолжение обсуждения Округление процентов в опросах неточно:

Я решил создать новую тему, так как последняя проблема связана не с сомнительными правилами округления, а с совершенно неверными правилами. Поэтому эта тема должна быть в категории Contribute > Bug. Это не критичная ошибка, но всё же довольно неудобная.

У нас был публичный опрос, в котором проголосовало 106 человек. 69 голосов было подано за «нет» и 37 за «да». Простое деление показывает, что 69/106 составляет примерно 65,09 %. Однако система показывает, что 66 % проголосовали за «нет» и 34 % за «да». Вот скриншот:

Итоговый результат составил 72 голоса за «нет» и 38 за «да», и опрос теперь закрыт. 72/110 составляет примерно 65,45 %, что система также округляет до 66 %, так что вы можете увидеть эту ошибку в действии на Tappara.co.

Я пытался посмотреть код, чтобы найти причину ошибки, но не смог. Я обнаружил, что вызывается функция с именем evenRound, но при поиске её исходного кода я её не нашёл. Это просто предположение, но, возможно, эта функция округляет число до ближайшего чётного целого?

Not sure I understand what’s broken here. The poll will round the % so they add up to 100%.

1 лайк

Результат ужасный, именно в этом и проблема. :slight_smile:

Спасибо за код. Я просто не смог его найти, что лишь подтверждает, насколько я не умею пользоваться GitHub. Код взят со Stack Overflow, где его автор пишет:

Я не уверен, какая точность вам нужна, но я бы просто прибавил единицу к первым n числам, где n — это ceil от общей суммы дробных частей. В данном случае это 3, поэтому я бы прибавил 1 к первым трём элементам, а остальные округлил вниз. Конечно, это не супер точно: некоторые числа могут быть округлены вверх или вниз не там, где нужно, но работает это неплохо и всегда даёт в сумме 100%.

Неудивительно, что при таком грубом методе получаются странные результаты. Я бы предложил быть чуть более изощрёнными. Этот пост в той же теме имеет 72 апвоута, в то время как пост, откуда взят код, — всего 1.

Существует множество способов сделать это, если вас не волнует зависимость от исходных десятичных данных.

Первый и, возможно, самый популярный метод — это метод наибольших остатков.

Который по сути выглядит так:

  1. Округляем всё вниз
  1. Вычисляем разницу между суммой и 100
  1. Распределяем эту разницу, прибавляя по 1 к элементам в порядке убывания их дробных частей

Я думаю, этот метод был бы лучшим выбором. Он всё ещё очень прост и даёт гораздо лучшие результаты. Текущему коду нужно добавить ещё несколько строк, чтобы указать функции, какие числа следует округлять вверх. Однако есть ещё один нюанс. Из этого поста в той же теме:

Хорошо проголосованный ответ Варуна Вохры минимизирует сумму абсолютных ошибок, и его очень просто реализовать. Однако есть граничные случаи, которые он не обрабатывает — каков должен быть результат округления 24,25, 23,25, 27,25, 25,25? Одно из этих чисел нужно округлить вверх вместо вниз.

В посте предлагаются различные методы выбора того, какое число округлять вверх, а какое вниз, но полностью избежать произвольного выбора невозможно. Если вы стремитесь к совершенству, можете прочитать этот пост дальше, но в этих редких особых случаях меня бы устроил и этот вариант.

Вы, вероятно, просто произвольно выберете первый или последний элемент в списке.

Надеюсь, это поможет. Мои навыки, вероятно, позволили бы мне внести улучшения самостоятельно, если бы я сначала изучил базовый синтаксис. Сейчас я этим заниматься не буду, но, возможно, когда-нибудь, если кто-то не займётся этим раньше.

P.S. Я немного улучшил заголовок, заменив слово “broken” на “imprecise”. Эту тему также следует перенести в Contribute > Feature или объединить с предыдущей темой.

4 лайка

Feel free to submit a pull request to change the rounding algorithm :wink:

5 лайков

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.

https://github.com/rizka10/discourse/pull/1/files

2 лайка

Looks fine to me. Would you mind making it a PR so that I can merge it?

I thought that was a pull request already… I’m as confused as always with Github. :confused:
I clicked some more green buttons now, maybe it does the trick?

If you want to create a PR from your code, you should go there: https://github.com/discourse/discourse/compare/master...rizka10:master :wink:

1 лайк

I think I’m getting it now. I ran into some Github tutorials online yesterday and I should really go through one to learn the basics.

1 лайк

Awesome. Before we can merge the code, we’ll need you to sign the CLA. I promise, this is the last step (also, you only have to sign it once ;))

6 лайков

Thanks for all the help! I signed it now.

4 лайка