Decimal.Divide
От: peer  
Дата: 27.04.22 13:31
Оценка:
Непонятка какая-то с децимал.
такой простой код но не могу получить точность 5 знаков после запятой


decimal a = (считывается из базы и в переменной находится значение) 83.90080;
decimal b = (считывается из базы и в переменной находится значение) 83.90080;

var result = Dceimal.Round(Decimal.Divide(a, b), 5)

= 1

а надо 1.00000

как получить 5 знаков без умножения результата деления на 1.00000m?
Re: Decimal.Divide
От: fmiracle  
Дата: 27.04.22 14:03
Оценка: +2
Здравствуйте, peer, Вы писали:

P>decimal a = (считывается из базы и в переменной находится значение) 83.90080;

P>decimal b = (считывается из базы и в переменной находится значение) 83.90080;
P>var result = Dceimal.Round(Decimal.Divide(a, b), 5)

P>= 1

P>а надо 1.00000

Для decimal 1 и 1.00000 это в точности одно и то же.

Ты, наверное, хочешь чтобы в выводе на экран было 1.00000? Но это просто строковое представление. Так что надо задать правило форматирования в строку:

var str1 = string.Format("{0:#.00000}", a);


или так

var str2 = $"{a:#.00000}";
Re: Decimal.Divide
От: · Великобритания  
Дата: 27.04.22 16:22
Оценка: -3
Здравствуйте, peer, Вы писали:

P>Непонятка какая-то с децимал.

P>= 1
P>а надо 1.00000
Это потому что decimal это floating point, а ты, похоже, ожидаешь fixed point. Погляди в сторону SqlDecimal:

SqlDecimal fa = SqlDecimal.Parse("83.90080");
SqlDecimal fb = SqlDecimal.Parse("83.90080");
Console.WriteLine(SqlDecimal.ConvertToPrecScale(SqlDecimal.Divide(fa, fb), fa.Precision, fa.Scale));
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Decimal.Divide
От: fmiracle  
Дата: 27.04.22 20:06
Оценка:
Здравствуйте, ·, Вы писали:

P>>Непонятка какая-то с децимал.

P>>= 1
P>>а надо 1.00000
·>Это потому что decimal это floating point, а ты, похоже, ожидаешь fixed point. Погляди в сторону SqlDecimal:

Decimal это не floating point. У него как раз все ровно поделилось, и незначащие нули по умолчанию не выводятся (откуда знать что их надо ровно 5?)
Re[3]: Decimal.Divide
От: · Великобритания  
Дата: 27.04.22 20:22
Оценка:
Здравствуйте, fmiracle, Вы писали:

F>·>Это потому что decimal это floating point, а ты, похоже, ожидаешь fixed point. Погляди в сторону SqlDecimal:

F>Decimal это не floating point. У него как раз все ровно поделилось, и незначащие нули по умолчанию не выводятся
За это минусы? Доки я тут один что-ли читаю?..

Decimal Struct — Represents a decimal floating-point number.

(c) https://docs.microsoft.com/en-us/dotnet/api/system.decimal

Вы наверное с double путаете, который binary floating-poing.

F>(откуда знать что их надо ровно 5?)

Это и есть смысл "fixed point".
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Decimal.Divide
От: Ночной Смотрящий Россия  
Дата: 28.04.22 09:54
Оценка:
Здравствуйте, ·, Вы писали:

·>За это минусы? Доки я тут один что-ли читаю?..

·>

Decimal Struct — Represents a decimal floating-point number.


Минусы тебе не за floating-point, а за то что те проблемы неточности представления вроде бы точных цифр, на которые ты намекаешь, это не из-за плавучей точки, а из-за того что в классических float/double число представленно в двоичном виде, и преобразование его в десятичный порождает иррациональные числа на ровном месте. Т.е. просто сохранив в float рациональное число, даже без каких либо операций, а потом прочитав его и преобразовав в десятичное, ты можешь получить число иррациональное.
Так вот, суть decimal как раз в том что оно представляется в памяти именно десятичным, поэтому если десятичное число рационально, но оно таковым и останется.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[5]: Decimal.Divide
От: · Великобритания  
Дата: 28.04.22 10:20
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>·>За это минусы? Доки я тут один что-ли читаю?..

НС>·>

Decimal Struct — Represents a decimal floating-point number.

НС>Минусы тебе не за floating-point, а за то что те проблемы неточности представления вроде бы точных цифр, на которые ты намекаешь,
Я ни на что не намекал. Я сказал, что если требуется фиксированное число знаков после запятой, то лучше использовать тип с фиксированной точкой, коим является SqlDecimal.

НС> это не из-за плавучей точки, а из-за того что в классических float/double число представленно в двоичном виде,

Про двоичный вид это вы сами придумали. В моём заминусованном сообщении не было ничего о двоичных.

НС> и преобразование его в десятичный порождает иррациональные числа на ровном месте. Т.е. просто сохранив в float рациональное число, даже без каких либо операций, а потом прочитав его и преобразовав в десятичное, ты можешь получить число иррациональное.

Причём тут иррациональные числа?!! Что-то тут похоже все всю школьную математику забыли.

НС>Так вот, суть decimal как раз в том что оно представляется в памяти именно десятичным, поэтому если десятичное число рационально, но оно таковым и останется.

Суть в том, что SqlDecimal тоже десятичное. Но не то же самое что Decimal struct.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[6]: Decimal.Divide
От: Ночной Смотрящий Россия  
Дата: 28.04.22 10:52
Оценка:
Здравствуйте, ·, Вы писали:

НС>>Минусы тебе не за floating-point, а за то что те проблемы неточности представления вроде бы точных цифр, на которые ты намекаешь,

·>Я ни на что не намекал. Я сказал, что если требуется фиксированное число знаков после запятой, то лучше использовать тип с фиксированной точкой

Нет, не лучше. И вопрос был вообще не про это.

·>, коим является SqlDecimal.


Серьезно? Использовать тип из пакетов драйвера для MSSQL?

НС>> это не из-за плавучей точки, а из-за того что в классических float/double число представленно в двоичном виде,

·>Про двоичный вид это вы сами придумали.

Это я не придумал, а пояснил, откуда странное поведение float типов и почему decimal этих проблем лишен.

НС>> и преобразование его в десятичный порождает иррациональные числа на ровном месте. Т.е. просто сохранив в float рациональное число, даже без каких либо операций, а потом прочитав его и преобразовав в десятичное, ты можешь получить число иррациональное.

·>Причём тут иррациональные числа?!!

При том что иррациональное число в обычных форматах потребует для хранения бесконечного объема памяти. Поэтому такое число округляется до ближайшего рационального. В результате двух неявных преобразований десятичное->двоичное->десятичное получаем ошибку и числа на выходе вроде 0.5999999986 вместо 0.6.

НС>>Так вот, суть decimal как раз в том что оно представляется в памяти именно десятичным, поэтому если десятичное число рационально, но оно таковым и останется.

·>Суть в том, что SqlDecimal тоже десятичное.

Суть в том что SqlDecimal это часть драйвера MSSQL, описывающая целевой тип decimal в нем. И в данном конкретном случае он полностью не нужен, так как и обычный дотнетный decimal будет вести себя точно так же. Вот правильный ответ — Re: Decimal.Divide
Автор: fmiracle
Дата: 27.04.22
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[7]: Decimal.Divide
От: fmiracle  
Дата: 28.04.22 11:14
Оценка: +1
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Суть в том что SqlDecimal это часть драйвера MSSQL, описывающая целевой тип decimal в нем. И в данном конкретном случае он полностью не нужен, так как и обычный дотнетный decimal будет вести себя точно так же. Вот правильный ответ — Re: Decimal.Divide
Автор: fmiracle
Дата: 27.04.22


Ну не совсем точно так же, есть небольшая разница. В SqlDecimal есть возможность указать положение точки явно через ConvertToPrecScale (потому что в sql server оно задается явным образом), а в дотнетном decimal оно всегда выбирается автоматически. Про это и говорит ·.

Только вот исходя из формулировки стартового топика, я не думаю, что топик-стартеру нужно было именно это. А тащить классы специфичные для mssql в несвязанные с ним задачи, даже если это студенческий практикум — бе.
Re[2]: Decimal.Divide
От: peer  
Дата: 28.04.22 11:23
Оценка:
Здравствуйте, fmiracle, Вы писали:

F>Здравствуйте, peer, Вы писали:


P>>decimal a = (считывается из базы и в переменной находится значение) 83.90080;

P>>decimal b = (считывается из базы и в переменной находится значение) 83.90080;
P>>var result = Dceimal.Round(Decimal.Divide(a, b), 5)

P>>= 1

P>>а надо 1.00000

F>Для decimal 1 и 1.00000 это в точности одно и то же.


F>Ты, наверное, хочешь чтобы в выводе на экран было 1.00000? Но это просто строковое представление. Так что надо задать правило форматирования в строку:


надо на фронт передавать 1.00000 чтобы фронт просто отображал
Re[7]: Decimal.Divide
От: · Великобритания  
Дата: 28.04.22 11:50
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>>>Минусы тебе не за floating-point, а за то что те проблемы неточности представления вроде бы точных цифр, на которые ты намекаешь,

НС>·>Я ни на что не намекал. Я сказал, что если требуется фиксированное число знаков после запятой, то лучше использовать тип с фиксированной точкой
НС>Нет, не лучше. И вопрос был вообще не про это.
Именно про это. Ему нужны числа с 5 знаками после запятой.

НС>·>, коим является SqlDecimal.

НС> Серьезно? Использовать тип из пакетов драйвера для MSSQL?
Как я понял, топикстартер и так значения из базы берёт. Впрочем, как я понял, этот тип не из пакета драйверов MSSQL, а часть .net framework.

НС>>> это не из-за плавучей точки, а из-за того что в классических float/double число представленно в двоичном виде,

НС>·>Про двоичный вид это вы сами придумали.
НС>Это я не придумал, а пояснил, откуда странное поведение float типов и почему decimal этих проблем лишен.
Круто, конечно. Но это никакого отношения к обсуждаемой проблеме не имеет. Мы обсуждаем float-point vs fixed-point арифметику _десятичных_ чисел. Причём тут двоичные числа-то?!!

НС>>> и преобразование его в десятичный порождает иррациональные числа на ровном месте. Т.е. просто сохранив в float рациональное число, даже без каких либо операций, а потом прочитав его и преобразовав в десятичное, ты можешь получить число иррациональное.

НС>·>Причём тут иррациональные числа?!!
НС>При том что иррациональное число в обычных форматах потребует для хранения бесконечного объема памяти. Поэтому такое число округляется до ближайшего рационального. В результате двух неявных преобразований десятичное->двоичное->десятичное получаем ошибку и числа на выходе вроде 0.5999999986 вместо 0.6.
Почитай что такое иррациональные числа. 1/3 — число рациональное, но непредставимо ни в виде десятичной записи, ни в виде двоичной, хотя не требует бесконечного объёма памяти, т.к. можно представить в троичной записи.

НС>>>Так вот, суть decimal как раз в том что оно представляется в памяти именно десятичным, поэтому если десятичное число рационально, но оно таковым и останется.

НС>·>Суть в том, что SqlDecimal тоже десятичное.
НС>Суть в том что SqlDecimal это часть драйвера MSSQL, описывающая целевой тип decimal в нем.
Есть два типа арифметики "fixed point" и "float point" и есть два типа представления чисел binary и decimal. И возможны все четыре комбинации. Можно такую табличку составить:

Fixed-point Float-point
decimal SqlDecimal decimal struct
binary not in .net float, double
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Decimal.Divide
От: Mihas  
Дата: 28.04.22 12:07
Оценка: -1
Здравствуйте, peer, Вы писали:

P>как получить 5 знаков без умножения результата деления на 1.00000m?

А попробуй так
var result = 0.00000m;
result = Dceimal.Round(Decimal.Divide(a, b), 5);
Re[3]: Decimal.Divide
От: · Великобритания  
Дата: 28.04.22 12:50
Оценка:
Здравствуйте, peer, Вы писали:

P>надо на фронт передавать 1.00000 чтобы фронт просто отображал

Передавай на фронт строку. Браузеры тоже не умеют в fixed point, надо будет самому реализовывать или либу какую-нибудь искать.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[7]: Decimal.Divide
От: · Великобритания  
Дата: 29.04.22 08:50
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Это я не придумал, а пояснил, откуда странное поведение float типов и почему decimal этих проблем лишен.

Кстати, не лишен. decimal тоже теряет точность:
var n = "1000000000000000000000000.00001";
var flt = decimal.Parse(n);
var fxd = SqlDecimal.Parse(n);
Console.WriteLine(flt);// выводит 1000000000000000000000000.0000
Console.WriteLine(fxd);// выводит 1000000000000000000000000.00001

float типы по определению теряют точность. Просто double теряет двоичные знаки, а decimal — десятичные.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: Decimal.Divide
От: Ночной Смотрящий Россия  
Дата: 29.04.22 20:39
Оценка:
Здравствуйте, ·, Вы писали:

НС>>Это я не придумал, а пояснил, откуда странное поведение float типов и почему decimal этих проблем лишен.

·>Кстати, не лишен. decimal тоже теряет точность:

Он теряет ее предсказуемым и очевидным образом.

·>float типы по определению теряют точность.


Любой тип теряет точность, в том числе и fixed point.

·> Просто double теряет двоичные знаки, а decimal — десятичные.


Еще раз — проблема не в этом. Проблема в том, что если мы возьмем число, которое в десятичном представлении содержит заведомо меньшее количество знаков, чем точность, потом сохраним его в float, а потом опять получим десятичное представление, то результат может отличаться. А вот в decimal такой ситуации не будет, не смотря на то что точка там плавающая. А в исходном примере плавающая точка или нет — вообще все равно, потому что там нигде не происходит плаванья той самой точки.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.