Функция Round

Обсуждение Stimulsoft Reports.NET
GerBert
Сообщения: 2
Зарегистрирован: 24 мар 2023, 14:53

Re: Функция Round

Сообщение GerBert »

Aleksey писал(а): 27 мар 2023, 21:45 Здравствуйте,

Используйте следующее выражение:
{Round(20.275m,2)}

Спасибо.
Добрый день.
В этом случае округление идёт до ближайшего чётного, т.е. 20.275 округлится до 20.28, а вот 20.665 округлится опять в меньшую сторону до 20.66
А если ещё внутри Round использовать не число, а выражение, SumIf например, то при использовании m будет синтаксическая ошибка.
Aleksey
Сообщения: 2906
Зарегистрирован: 22 апр 2010, 06:57

Re: Функция Round

Сообщение Aleksey »

Здравствуйте,

Round(20.665m,2) округляется до 20.67, а вот Round(20.665,2) округляется до 20.66.

Главная причина проблемы - это недостаточная точность типов Double и Float.
вместо числа 20.665 на самом деле может храниться что-то типа 20.664999999999998
соответственно и округление не срабатывает правильно.
проблема описана например тут:
https://stackoverflow.com/questions/432 ... zero-81-72
а также тут (в разделе "Округление и точность"):
https://learn.microsoft.com/ru-ru/dotne ... em_Double_

> А если ещё внутри Round использовать не число, а выражение, SumIf например, то при использовании m будет синтаксическая ошибка.

m - это постфикс только для чисел.
для выражений надо использовать приведение типов, например:
Round((decimal)SumIf(...),2)

Спасибо.
Аватара пользователя
molochnii
Сообщения: 56
Зарегистрирован: 29 июл 2021, 14:43

Re: Функция Round

Сообщение molochnii »

Aleksey писал(а): 31 мар 2023, 18:40 m - это постфикс только для чисел.
для выражений надо использовать приведение типов, например:
Round((decimal)SumIf(...),2)
Здравствуйте 8-) , я правильно понимаю, что настройка "StiOptions.Engine.MidpointRounding = MidpointRounding.AwayFromZero;" будет работать только для типа decimal? :geek:
Поясню свою ситуацию - мне нужно поставить по умолчанию использование округления по стратегии MidpointRounding.AwayFromZero, но применение этой настройки не привело к ожидаемому результату. Округление использует эту стратегию , только если я указываю ее строго в каждый ячейке, по типу "{Math.Round((double)Data.data1, 3, MidpointRounding.AwayFromZero)}", где Data.data1 это дробное значение типа double nullable - в таком случае я получаю ожидаемый результат.

:ugeek: Если настройка StiOptions.Engine.MidpointRounding работает только для decimal, то это кажется мне странным и не практичным, в таком случае мне придется каждый раз при отрисовке значения приводить его к decimal, что докучи может вызвать ещё и необработанное исключение, в случае если значение будет выходить за пределы допустимых значений для decimal (т.к. размер хранимых значений для decimal меньше чем для double). :? Конечно, можно сказать, что если значение выходит за пределы размерности decimal, то это немного странно, ведь оперировать такими значениями врядли имеет какой то смысл. Однако такие данные бывают у нас в системе, и пускай их нельзя назвать валидными, но просто избавиться от них мы не можем, в таком случае придется ещё добавлять проверки на валидность данных перед их конвертацией.. :roll: В общем внедрение decimal для нашей системы будет не самое приятное занятие. :(
Аватара пользователя
molochnii
Сообщения: 56
Зарегистрирован: 29 июл 2021, 14:43

Re: Функция Round

Сообщение molochnii »

Дополнительно хотелось бы узнать - какая стратегия округление применяется по умолчанию в ячейке (т.е. когда я НЕ указываю округление в формуле, но указываю формат "числовой" и кол-во знаков после запятой = 3; привел пример как выглядит ячейка ниже) - для версий: 2024.3.1; 2019.2.2.0?
Потому что, в двух этих версиях сейчас у нас выводятся разные результаты при округлении одного и того же значения, без указания явного округления, но с указанием формата ячейки как описано выше в уточнении.

Например число 0.0045 при указании в формате количества знаков после запятой = 3 - в версии 2019.2.2.0 округляется до 0.005, что соответствует стратегии округления AwayFromZero; в то время как в версии 2024.3.1 мы получаем уже число 0.004, что соответствует стратегии банкирского округления.

P.S. Хотя я не уверен, что дело тут именно в стимулсофте, потому что кроме этого меняется ещё и сама платформа (вместо .net framework 4.8 - .net 6.0). Однако хотелось бы уточнить :geek:

Пример как выглядит ячейка, результат округления в которой отличается в разных версиях стимулсофта:
<Text43 Ref="62" type="Text" isKey="true">
<Border>All;Black;1;Solid;False;4;Black</Border>
<Brush>Transparent</Brush>
<ClientRectangle>0,0,1.3,0.3</ClientRectangle>
<Conditions isList="true" count="0" />
<DockStyle>Left</DockStyle>
<Expressions isList="true" count="0" />
<Font>Tahoma,7</Font>
<Guid>6b22cd3247744d1da2d7728183ed3f2e</Guid>
<HorAlignment>Right</HorAlignment>
<Margins>2,2,0,0</Margins>
<MinSize>0,0.3</MinSize>
<Name>Text43</Name>
<Page isRef="8" />
<Parent isRef="61" />
<Text>{IIF(Data.t1 == null, "-", Data.t1)}</Text>
<TextBrush>Black</TextBrush>
<TextFormat Ref="63" type="NumberFormat" isKey="true">
<DecimalDigits>3</DecimalDigits>
<DecimalSeparator>,</DecimalSeparator>
<GroupSeparator> </GroupSeparator>
<NegativePattern>1</NegativePattern>
<State>DecimalDigits, DecimalSeparator</State>
</TextFormat>
<Type>Expression</Type>
<VertAlignment>Center</VertAlignment>
</Text43>
Аватара пользователя
molochnii
Сообщения: 56
Зарегистрирован: 29 июл 2021, 14:43

Re: Функция Round

Сообщение molochnii »

Интересные подробности. Тестирование на билде 2024.3.1 на платформе net 6.0
Вложения
Standart.mrt
Файл для тестирования
(69.94 КБ) 162 скачивания
Как видно на скрине, изменение StiOptions.Engine.MidpointRounding вообще ничего не дает, ни для double, ни для decimal, но вот изменение типа данных перед их выводом меняет результат округления, как и формат ячейки - числовой либо общий. Просьба пояснить как это работает и как это использовать?<br /><br />Ещё разок - не понятно именно то, почему не работает настройка MidpointRounding; почему для decimal всегда результат отличный от double; почему приведение к общему формату приводит к отличиям от числового формата?
Как видно на скрине, изменение StiOptions.Engine.MidpointRounding вообще ничего не дает, ни для double, ни для decimal, но вот изменение типа данных перед их выводом меняет результат округления, как и формат ячейки - числовой либо общий. Просьба пояснить как это работает и как это использовать?

Ещё разок - не понятно именно то, почему не работает настройка MidpointRounding; почему для decimal всегда результат отличный от double; почему приведение к общему формату приводит к отличиям от числового формата?
Снимок экрана 2024-06-24 181926.png (118.67 КБ) 3791 просмотр
Aleksey
Сообщения: 2906
Зарегистрирован: 22 апр 2010, 06:57

Re: Функция Round

Сообщение Aleksey »

Здравствуйте,

> я правильно понимаю, что настройка "StiOptions.Engine.MidpointRounding = MidpointRounding.AwayFromZero;" будет работать только для типа decimal?

Нет. Посмотрите еще раз статьи по ссылкам.
Функция Round - это округление до указанного ДЕСЯТИЧНОГО знака. У неё есть перегрузки и для типа double, и для типа decimal.
Тип Decimal хранит число сразу в десятичном виде. Т.е. если вы записали в него 20.275, то там так и будут записаны именно эти цифры. И поэтому округление для типа decimal всегда работает правильно.
А вот в типах float и double число хранится в двоичном виде, и преобразуется к десятичному только при отображении на экране. В этих типах невозможно точно записать десятичные числа, они всегда будут иметь какую-то погрешность. И вот из-за этой погрешности функция Round не всегда правильно определяет последнюю цифру, и соответственно неправильно округляет.

Пример внутренней записи чисел:
20.275 -> 20.274999999999998578
20.665 -> 20.664999999999999147
0.0045 -> 0.0044999999999999996
Первое из чисел имеет большую погрешность, поэтому для него округление неправильно срабатывает.

А вот преобразование чисел из float/double в decimal реализовано в NetFramework очень хорошо, оно почти всегда даёт правильный результат.
Поэтому для точных расчётов мы всегда сначала приводим числа к формату decimal, а уже потом работаем с ними.

> Дополнительно хотелось бы узнать - какая стратегия округление применяется по умолчанию в ячейке - для версий: 2024.3.1; 2019.2.2.0?

Мы проверили обе этих версии - и не нашли разницы.
Возможно вы не установили свойство StiOptions.Engine.MidpointRounding в одном из случаев, поэтому и получили разные результаты.

Спасибо.
Аватара пользователя
molochnii
Сообщения: 56
Зарегистрирован: 29 июл 2021, 14:43

Re: Функция Round

Сообщение molochnii »

Спасибо за пояснения, стало действительно понятнее :D . Однако все ещё не могу понять, почему стратегии округления из настройки "StiOptions.Engine.MidpointRounding" не работают. Давайте разберем сформированный на net 6.0 (стимулсофт версии 2024.2.6) отчет. :geek:

По столбцам: Общий формат - число, приведенное к общему формату по маске "0.000;-0.000;0.000"; Полное значение - непосредственное значение с которым мы работаем, выведенное без округления; AwayFrowZero - значение, округленное внутри формулы ячейки через Math.Round по стратегии AwayFromZero; ToEven - значение, округленное внутри формулы ячейки через Math.Round по стратегии ToEven; Числовой формат - в формуле никак не преобразуем значение, но для ячейки проставляем формат числовой и указываем 3 знака после запятой. Просто округление - значение, округленное внутри формулы ячейки через Math.Round, без явного указания стратегии округления.

:ugeek: Итак перейдем к разбору. Для net 6.0 видно, что значения меняются только в столбце "Числовой формат", что в целом логично, хотя по идее в столбце "Просто округление" тоже должны были бы менять значения, т.к. там мы не указываем конкретную стратегию, и она должна была бы подтянуться из "StiOptions.Engine.MidpointRounding". В столбце "Числовой формат" значения меняются только с изменением типа данных - с double на decimal, как вы и сказали, это происходит потому, что double считает, что 0.00045 это 0.00449999... что меньше чем 0.0045, по этому округляется до 0.004 - хорошо, но почему в таком случае настройка "StiOptions.Engine.MidpointRounding" не влияет на результат округления для decimal :?: Ведь видно в столбце "AwayFromZero" и "ToEven" - что результат округления мы получаем всегда такой, какой предполагается стратегией округления, значит я делаю вывод, что при использовании числового формата происходит округление без учета настройки "StiOptions.Engine.MidpointRounding" :?:
:ugeek: Разбираем далее. В столбце "Просто округление" - такое ощущение что всегда применяется стратегия ToEven (что является стандартом для функции Math.Round), из чего я делаю вывод, что и функция Math.Round не принимает настройку "StiOptions.Engine.MidpointRounding", а так же я делаю вывод, что у вас прописано по умолчанию округление по стратегии AwayFromZero для применения формата, как для общего формата (что видно по столбцу "Общий формат"), так и для числового формата. А вот Math.Round вы переопределять стратегию по умолчанию не стали, по этому для decimal мы имеем разные результаты в столбцах "Числовой формат" (+ "Общий формат") и "Просто округление". :o

Может быть я не правильно сделал выводы, но в любом случае "StiOptions.Engine.MidpointRounding" не меняет результат, либо я не понимаю как использовать эту настройку. :roll:
Вложения
Исходник МРТ файла можно найти в сообщении выше
Исходник МРТ файла можно найти в сообщении выше
Снимок экрана 2024-06-25 102312.png (73.37 КБ) 3778 просмотров
Последний раз редактировалось molochnii 25 июн 2024, 11:01, всего редактировалось 2 раза.
Аватара пользователя
molochnii
Сообщения: 56
Зарегистрирован: 29 июл 2021, 14:43

Re: Функция Round

Сообщение molochnii »

Но дальше больше :cry: . Этот отчет по тому же шаблону сформирован на net framework 4.8 (версия стимулсофта 2019.2.2). Первое что бросается в глаза - отсутствие надписи в заголовке по настройке "StiOptions.Engine.MidpointRounding", ну оно и понятно - тогда этой настройки ещё не было. Далее - единственное значение которое изменилось по отношению к net 6.0 - в столбце "Числовой формат" (сразу скажу, что именно из-за этого изменения мы сейчас все это обсуждаем), мы видим что для double значение изменилось с 0.004 на 0.005 - от сюда три вывода :!: , один из которых верен - либо версия с тем, что 0.0045 преобразуется в 0.00449999... не верна (и в таком случае в предыдущем разборе больше проблем чем описано), либо просто net framework по другому обрабатывает эту ситуацию (в таком случае нам просто нужно сделать так, что бы net 6.0 вел себя аналогичным образом - любым способом), либо вы как-то переопределили поведение для числового формата (в таком случае нужно для начала понять в чем отличие)

Надеюсь, я корректно донес свои мысли и вы разобрались в том, что я имел ввиду :)
Вложения
Снимок экрана 2024-06-25 102312.png
Снимок экрана 2024-06-25 102312.png (71.84 КБ) 3777 просмотров
Aleksey
Сообщения: 2906
Зарегистрирован: 22 апр 2010, 06:57

Re: Функция Round

Сообщение Aleksey »

Здравствуйте,

Спасибо за детальное описание проблемы. Нужно время для анализа.

Спасибо.
vea
Сообщения: 21
Зарегистрирован: 01 июл 2024, 11:47

Re: Функция Round

Сообщение vea »

Здравствуйте. Сколько времени вам требуется на ответ? Аналогичный вопрос по теме отрицательных нулей (viewtopic.php?p=37713#p37713)
Ответить