Введение
В версии .NET Core 3.0 были внесены изменения в синтаксический анализ и форматирование с плавающей точкой для соответствия стандарту IEEE 754-2008. Подробнее обо всех изменениях можно прочитать в этой статье. В контексте работы с продуктами Stimulsoft эти изменения чаще всего проявляются при округлении чисел и в появлении «отрицательного» нуля.Отрицательный ноль
Отрицательный и положительный ноль используются в математическом анализе как условные обозначения отрицательной и положительной бесконечно малой величины. В типичных задачах программирования достаточно обычного нуля, но в некоторых продвинутых математических расчётах такие величины требуется. Для этой цели и был добавлен «отрицательный ноль».Отрицательный ноль получается в результате округления отрицательных чисел. Например, результат выполнения операции Math.Round(-0.01, 0) ранее был «0», а теперь именно «-0». Также это проявляется при форматировании чисел (форматы Number, Currency, Percent), так как в процессе форматирования применяется округление. С точки зрения математики, это более правильно. Но с точки зрения программирования это «breaking change», потому что изменяется стандартное поведение операции округления, и нет простого способа отключить это изменение.
Для устранения проблемы с появлением отрицательного нуля мы сделали доработку в наших продуктах серии .NET Core, и теперь по умолчанию отрицательный ноль заменяется на обычный. Однако, если ваши вычисления всё-таки предполагают наличие отрицательного нуля, вы можете установить опцию StiOptions.Engine.AllowNegativeZero = true.
Округление чисел
Одна из доработок в .NET Core 3.0 – это изменения в синтаксическом анализе чисел с плавающей запятой. Почему доработка была необходима: в типах float и double число хранится в двоичном виде, и преобразуется к десятичному только при округлении и при отображении на экране. Например, число «0.0045» на самом деле хранится в памяти примерно в таком виде: «0.0044999999999999996». И уже перед выводом на экран специальный метод «восстанавливает» исходное число из этого двоичного представления. Поэтому в типах float и double невозможно точно записать десятичные числа – они всегда будут иметь какую-то погрешность.Ранее алгоритмы этих преобразований чисел отличались от используемых в стандарте IEEE 754-2008. Поэтому на разных системах могли получаться разные результаты вычислений. Теперь результаты вычислений соответствуют стандарту. Но эти результаты могут отличаться от тех, которые были раньше. Это оказывает влияние на работу стандартной функции Round, результат работы которой теперь тоже может отличаться.
Это вызвало проблемы у некоторых пользователей, которые имеют версии своих продуктов на .NET Framework и .NET и получают разные результаты в этих версиях. Обратите внимание!
Чтобы результаты вычислений были везде одинаковыми и прогнозируемыми, мы советуем использовать следующий способ: во всех важных вычислениях приводить типы к Decimal, это обеспечит корректное округление в любых вычислениях. Модифицировать несколько отчётов несложно. Но некоторые пользователи столкнулись с необходимостью переделывать множество ранее созданных отчетов. Для этого случая мы добавили новую опцию:
StiOptions.Engine.ForceConversionToDecimalInTextFormat = false;
По умолчанию опция выключена. Если установить её в true, то при форматировании текста в форматах Number, Currency and Percentage аргументы типа float и double будут автоматически конвертироваться в Decimal для повышения точности округления.Параметр MidpointRounding
Для получения банковского округления надо в стандартной функции Math.Round использовать параметр MidpointRounding.AwayFromZero. Для удобства пользователей мы ранее добавили этот параметр и в нашу функцию Round. По просьбам пользователей, мы также сделали возможность задать значение этого параметра по умолчанию, чтобы не надо было изменять все шаблоны отчётов. Для этого можно установить значение следующей опции:StiOptions.Engine.MidpointRounding = MidpointRounding.AwayFromZero;
Таким образом, использование приведённых выше опций позволяет удобно управлять точностью расчетов и избежать некоторых проблем при переносе продукта на .NET.