Сообщество программистов Autodesk в СНГ
ADN Club => AutoCAD .NET API => Тема начата: Дмитрий Загорулькин от 24-03-2018, 21:07:49
-
Всем привет!
Словил весьма неприятный для себя баг в своём приложении. Оказывается, я уже много лет использовал метод Math.Round(double, int) (https://msdn.microsoft.com/ru-ru/library/75ks3aby(v=vs.110).aspx) до конца не понимая, как он работает. Беглый поиск по нашему форуму показал, что, возможно, не я один такой. Так что, возможно, кому-то будет полезна эта информация.
Суть работы этого метода подробно и с примерами описана в статье: http://aakinshin.net/ru/blog/post/cheatsheet-rounding/
В чём же была проблема? Оказывается, по умолчанию, у этого метода весьма странное поведение при округлении числа, если значение первого отбрасываемого разряда равно 5. Округление выполняется (!тадам!) в сторону ближайшего чётного числа! То есть, как будто используется метод Math.Round (Double, Int32, MidpointRounding) (https://msdn.microsoft.com/ru-ru/library/f5898377(v=vs.110).aspx) со значением mode = ToEven. На примере: если мы имеем число 1.125 и будем округлять его до 2 знаков после запятой, то получим 1.12, а не 1.13, как ожидается по тем правилам округления, которые мы изучали в школе.
Однако, если использовать double.ToString с параметром формата, то округление выполняется по умолчанию с режимом AwayFromZero. Например: 1.125.ToString("#.##") = "1.13".
-
Да есть такая штука. Я вот тоже уже давно хотел задать этот вопрос. И у меня было ещё как то так число 1.1256 если округлять до 2 знаков тоже давало 1.12
После этого я стал аккуратнее. Люди жалуются на разные результаты в civil одно а у меня на миллиметр другое. Но это не всегда.
-
В python как и в других языках тоже есть данная особенность. А вот в C# есть возможность указать по умолчанию режим округления? Вот в python например можно в начале указать способ округления чисел и проблем дальше нет.
В начале программы задаётся опция например:
getcontext().rounding = ROUND_HALF_UP
-
А вот в C# есть возможность указать по умолчанию режим округления?
Так же глобально, как в Python, наверное, нет. По крайней мере, я такого способа не знаю. Мне пришлось везде, где использовал этот метод, заменять его на перегрузку Math.Round (Double, Int32, MidpointRounding) и указывать MidpointRounding = AwayFromZero.
-
Мне пришлось везде, где использовал этот метод, заменять его на перегрузку Math.Round (Double, Int32, MidpointRounding) и указывать MidpointRounding = AwayFromZero.
Или можно было создать свой метод Math.RoundTrue(Double, Int32), который бы вызывал Math.Round (Double, Int32, MidpointRounding.AwayFromZero)
-
Я думал об этом, но я решил, что не так уж сложно третий параметр дописывать.
-
Тут бы через регулярное выражение автозамену сделать с дописыванием 3го параметра.
-
http://bash.im/quote/401148 :)
Серьёзно, проще ручками быстро пройтись по решению.
-
Согласен, когда объем мал то ручками проще. И анекдот в тему. :)
-
Ну, относительно мал:
Find all "Round", Match case, Whole word, Find Results 1, Entire Solution, ""
...
Matching lines: 35 Matching files: 19 Total files searched: 791
Т.е., 35 использований метода в 19 файлах. Но лично мне всё равно проще ручками пройтись, чем составлять регулярку, которая будет учитывать все возможные варианты, встречающиеся в коде. Надёжнее и спокойней.
-
если значение первого отбрасываемого разряда равно 5. Округление выполняется (!тадам!) в сторону ближайшего чётного числа
Это не баг - это такой метод округления. У нас им трассовики пользуются.
-
Да я не говорю, что это баг метода. Просто поведение оказалось не то, которое ожидаешь по умолчанию. И, в результате, появился баг моего приложения. К примеру, пользователь видит в свойствах объекта значение 1.71, а ему надо 1.7. Он исправляет на 1.7, но вместо этого получает 1.69. Он снова прописывает 1.7, объект изменяется и в свойствах выскакивает 1.71. Приходилось вбивать что-то типа 1.695, тогда, в итоге, получалось нужное значение 1.7. А всё потому, что в вычислениях составляющих этого свойства использовался этот метод округления и "звёзды сложились неудачно".
Хотел скринкастом показать, но что-то он глючит сегодня.
Вот как это выглядело: