Сообщение 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 практически бесполезно, вместо него надо брать фоннеймановское округление, которое для десятичной арифметики описывается так:
В вики почему-то тут существенные нюансы пропущены. 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 сразу вписал и метод округления...
M>Собсно, если кто не помнит — https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
M>Ну, и
Если у тебя десятичка, то 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 практически бесполезно, вместо него надо брать фоннеймановское округление, которое для десятичной арифметики описывается так:
В вики почему-то тут существенные нюансы пропущены. 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 сразу вписал и метод округления...
M>Собсно, если кто не помнит — https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
M>Ну, и
Если у тебя десятичка, то 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 сразу вписал и метод округления...