Информация об изменениях

Сообщение Re: Как изящнее реализовать банковское округление? от 15.03.2021 20:59

Изменено 15.03.2021 21:06 netch80

Re: Как изящнее реализовать банковское округление?
Здравствуйте, Marty, Вы писали:

M>Собсно, если кто не помнит — https://en.wikipedia.org/wiki/Rounding#Round_half_to_even

M>Ну, и парнокопытноепарное к нему — https://en.wikipedia.org/wiki/Rounding#Round_half_to_odd

Если у тебя десятичка, то round half to odd практически бесполезно, вместо него надо брать фоннеймановское округление, которое для десятичной арифметики описывается так:

Round to prepare for shorter precision: the candidate that is smaller in magnitude is selected, unless its voting digit has a value of either 0 or 5; in that case, the candidate that is greater in magnitude is selected.


В вики почему-то тут существенные нюансы пропущены. Voting digit — это минимальная из оставляемых в мантиссе цифр после уплотнения к допустимому количеству цифр (но ещё до финальной нормализации, если она делается).

M>Пока всё, что рожаю, выглядит как кусок говна.

M>Но наверное тоже можно просто и изящно сделать?

Пока что я увидел, что твой precisionShrinkTo тупо урезает хвост — так?
Доступные образцы используют jamming shift. (Надо поискать, как его для десятички использовать.)
Примерно так: сначала урезать так, чтобы было минимум 2 цифры поверх целевой точности, при этом, если урезанная часть не 0, а младшая из оставшихся 0, то заменяешь её на 1.
После этого смотришь на эти две дополнительные цифры: 00-49 — удаляешь; 51-99 — удаляешь с увеличением оставшихся на 1; 50 — смотришь на чётность остающейся цифры.

Если описывать всё это в округлении до целого — получается так:
1.49999 -> 1.49 -> 1
1.50000 -> 1.50 -> 2
1.50001 -> 1.51 -> 2
2.49999 -> 2.49 -> 2
2.50000 -> 2.50 -> 2
2.50001 -> 2.51 -> 3

Не думаю, что есть метод сильно проще.

M>void precisionShrinkTo( precision_t p ); // уменьшаем точность, что упало, то пропало — просто обрезаем

M>void precisionFitTo( precision_t p ); // тут как пойдёт

Я бы тут в API сразу вписал и метод округления...
Re: Как изящнее реализовать банковское округление?
Здравствуйте, Marty, Вы писали:

M>Собсно, если кто не помнит — https://en.wikipedia.org/wiki/Rounding#Round_half_to_even

M>Ну, и парнокопытноепарное к нему — https://en.wikipedia.org/wiki/Rounding#Round_half_to_odd

Если у тебя десятичка, то round half to odd практически бесполезно, вместо него надо брать фоннеймановское округление, которое для десятичной арифметики описывается так:

Round to prepare for shorter precision: the candidate that is smaller in magnitude is selected, unless its voting digit has a value of either 0 or 5; in that case, the candidate that is greater in magnitude is selected.


В вики почему-то тут существенные нюансы пропущены. Voting digit — это минимальная из оставляемых в мантиссе цифр после уплотнения к допустимому количеству цифр (но ещё до финальной нормализации, если она делается).

M>Пока всё, что рожаю, выглядит как кусок говна.

M>Но наверное тоже можно просто и изящно сделать?

Пока что я увидел, что твой precisionShrinkTo тупо урезает хвост — так?
Доступные образцы используют jamming shift. (Надо поискать, как его для десятички использовать.)
Примерно так: сначала урезать так, чтобы было минимум 2 цифры поверх целевой точности, при этом, если урезанная часть не 0, а младшая из оставшихся 0, то заменяешь её на 1.
После этого смотришь на эти две дополнительные цифры: 00-49 — удаляешь; 51-99 — удаляешь с увеличением оставшихся на 1; 50 — смотришь на чётность остающейся цифры.

Если описывать всё это в округлении до целого — получается так:
1.49999 -> 1.49 -> 1
1.50000 -> 1.50 -> 2
1.50001 -> 1.51 -> 2
2.49999 -> 2.49 -> 2
2.50000 -> 2.50 -> 2
2.50001 -> 2.51 -> 3

Не думаю, что есть метод сильно проще. UPD: хм, кажется, и одной цифры хватит, но если jamming shift реализовывать как "5 менять на 6, если отброшенный хвост ненулевой".

M>void precisionShrinkTo( precision_t p ); // уменьшаем точность, что упало, то пропало — просто обрезаем

M>void precisionFitTo( precision_t p ); // тут как пойдёт

Я бы тут в API сразу вписал и метод округления...