Гость
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / decimal.ToString() trim trailing zeros / 11 сообщений из 11, страница 1 из 1
09.12.2015, 23:49
    #39124515
Ex_Soft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
static void Main(string[] args)
{
      decimal
          decimalVictimI = 1.123456789010000m,
          decimalVictimII = 1.12345678901m;

      string
          decimalVictimIStr = decimalVictimI.ToString(), // "1.123456789010000"
          decimalVictimIIStr = decimalVictimII.ToString(); // "1.12345678901"
}


Почему, посмотрев IL, становится понятно:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
..method private hidebysig static void  Main(string[] args) cil managed
{
   .entrypoint
   // Code size       39 (0x27)
   .maxstack  6
   .locals init ([0] valuetype [mscorlib]System.Decimal decimalVictimI,
            [1] valuetype [mscorlib]System.Decimal decimalVictimII)
   IL_0000:  nop
   IL_0001:  ldc.i4     0x2ad45650
   IL_0006:  ldc.i4     0x3fdc7
   IL_000b:  ldc.i4.0
   IL_000c:  ldc.i4.0
   IL_000d:  ldc.i4.s   15
   IL_000f:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32,
                                                                      int32,
                                                                      int32,
                                                                      bool,
                                                                      uint8)
   IL_0014:  stloc.0
   IL_0015:  ldc.i4     0x28530435
   IL_001a:  ldc.i4.s   26
   IL_001c:  ldc.i4.0
   IL_001d:  ldc.i4.0
   IL_001e:  ldc.i4.s   11
   IL_0020:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32,
                                                                      int32,
                                                                      int32,
                                                                      bool,
                                                                      uint8)
   IL_0025:  stloc.1
   IL_0026:  ret
} // end of method Program::Main


decimal рожается посредством Decimal Constructor (Int32, Int32, Int32, Boolean, Byte)
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
public Decimal(int lo, int mid, int hi, bool isNegative, byte scale)
{
     if (scale > 0x1c)
     {
         throw new ArgumentOutOfRangeException("scale", Environment.GetResourceString("ArgumentOutOfRange_DecimalScale"));
     }
     this.lo = lo;
     this.mid = mid;
     this.hi = hi;
     this.flags = scale << 0x10;
     if (isNegative)
     {
         this.flags |= -2147483648;
     }
}


(BTW, decimal.Parse() тоже правильно выставляет scale). И вот, союзно этому scale, Decimal.ToString()
Код: c#
1.
2.
3.
4.
public override string ToString()
{
     return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo);
}


и возвращает значение за'PadRight'ченное нулями.
Дык к чему это все: можно ли как-то по умному заставить decimal "пересчитать" scale/flag? В контексте setter'а... Учитывая, что
Код: c#
1.
2.
Console.WriteLine("decimalVictimI {0}= decimalVictimII", decimalVictimI == decimalVictimII ? "=" : "!");
Console.WriteLine("{0}decimal.Equals(decimalVictimI, decimalVictimII)", decimal.Equals(decimalVictimI, decimalVictimII) ? string.Empty : "!");


Или только посредством чего-то а-ля
Код: c#
1.
decimalVictimI = decimal.Parse(decimalVictimI.ToString().TrimEnd(new[] {'0'}));


Можно, конечно, еще заюзать Decimal.GetBits() по умному, анализируя, scale и т.д. и т.п.
Но, мо, ЭстЪ что-то более вменяемое?

_________________
"Helo, word!" - 17 errors 56 warnings
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.12.2015, 07:56
    #39124585
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Ex_SoftИли только посредством чего-то а-ля
Код: c#
1.
decimalVictimI = decimal.Parse(decimalVictimI.ToString().TrimEnd(new[] {'0'}));


Нездоровый способ. В итоге две строки и массив символов создадутся и в мусор пойдут.
И криво отработает при
Код: c#
1.
decimalVictimI = 123000m



Пытался порешать этот же вопрос. В итоге перешел на double. У double ToString() без лишних нулей.

PS Если нет потребности в точности более 15 десятичных знаков, то double достаточно.
...
Рейтинг: 0 / 0
10.12.2015, 09:06
    #39124600
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Dima TPS Если нет потребности в точности более 15 десятичных знаков, то double достаточно.

грабли ждут тебя
...
Рейтинг: 0 / 0
10.12.2015, 09:32
    #39124620
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
ИзопропилDima TPS Если нет потребности в точности более 15 десятичных знаков, то double достаточно.

грабли ждут тебя
Если ты про равенство, то я в курсе.
...
Рейтинг: 0 / 0
10.12.2015, 11:24
    #39124685
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
при работе с double равенство вообще лучше не делать, только неравенство по модулю разницы с погрешностью
а вообще на работе часто сталкиваемся с этой проблемой, особенно когда данные надо импортнуть из Oracle в MS и обратно, в результате можно такого нагрузить
...
Рейтинг: 0 / 0
10.12.2015, 11:45
    #39124713
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Dima TЕсли ты про равенство, то я в курсе.
нет, я о накоплении погрешности
...
Рейтинг: 0 / 0
10.12.2015, 12:01
    #39124743
Axeleron
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Dima TИзопропилпропущено...


грабли ждут тебя
Если ты про равенство, то я в курсе.
Почитайте внимательно о double и decimal. Double не гаранирует точности после запятой. Там грабли не в точности более 15 десятичных знаков.
...
Рейтинг: 0 / 0
10.12.2015, 12:05
    #39124751
Ex_Soft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Dima TИ криво отработает при

Ну дык...
Ex_SoftИли только посредством чего-то а-ля

Знамо дело, что там поумнее надоть а-ля

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
         static class DecimalExtensions
         {
             public static decimal Normalize(this decimal value)
             {
                 if (value == 0)
                     return value;

                 var parts = Decimal.GetBits(value);

                 if ((byte)((parts[3] >> 16) & 0x7F) == 0)
                     return value;

                 var valueStr = value.ToString(CultureInfo.InvariantCulture);

                 //if (valueStr.IndexOf(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator, StringComparison.InvariantCulture) == -1)
                 //    return value;

                 var regex = new Regex(string.Format("(?<={0}\\d+)0+$", Regex.Escape(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator)));

                 return regex.IsMatch(valueStr) ? decimal.Parse(regex.Replace(valueStr, string.Empty), CultureInfo.InvariantCulture) : value;
             }
         }


Но это, тем паче на setter'е, еще хуже
Dima TНездоровый способ. В итоге две строки и массив символов создадутся и в мусор пойдут.


BTW, судя по google не один я был озадачен сим...

_________________
"Helo, word!" - 17 errors 56 warnings
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.12.2015, 13:07
    #39124856
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
AxeleronПочитайте внимательно о double и decimal. Double не гаранирует точности после запятой.
Тогда уж можно заявлять что вообще не гарантирует, т.к. точка там стоит после первого двоичного знака.

вики
Число двойной точности (Double precision, Double)

...
Окончательное значение числа равняется знак * ( 1 +мантисса/ 2^52) * 2^(порядок - 1023)
ИМХУ не надо путать с float с которым эти проблемы были актуальны.


У double мантисса 52 бита, т.е. максимум 2^52 = 4.5*10^15 вот откуда цифра 15 знаков взялась. И не важно до или после запятой. Первые 15 десятичных знаков будут хранится точно.

Точность 100% нужна только в денежном учете (т.е. в рублях). И то только при сложении и вычитании (еще при умножении на целое, но это тоже что и N сложений). Дальше начинаются погрешности из-за округления до копеек. Например: возьми практически любую счет-фактуру из 3-5 строк и проверь что в итого Сумма НДС = Сумма с НДС * Ставка НДС. Не сойдется на копейки. А если сойдется, то сложив построчно Сумму НДС не сойдется с итого Сумма НДС.
Т.е. как только начинается деление (или умножение на дробное) то об абсолютной точности можно забыть. decimal не спасет от округлений при выводе.
Изопропилнет, я о накоплении погрешности
Это надо много-много точных действий (сложение и вычитание) сделать чтобы погрешность исказила хотя бы на копейку результат в рублях. Или результат должен быть 15 знаков. Например сложить все входящие платежи в Сбербанк за год.
Если грубо прикинуть, кол-во операций не менее 10^(16 - макс.разрядность), т.е. например для получении ошибки при сложении до миллиарда надо сложить минимум 100000 значений. Ситуация конечно возможная, но очень редкая.
...
Рейтинг: 0 / 0
10.12.2015, 13:23
    #39124874
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Ex_SoftЗнамо дело, что там поумнее надоть а-ля
Код: c#
1.
2.
3.
4.
5.
         static class DecimalExtensions
         {
             public static decimal Normalize(this decimal value)
             {
...


ИМХУ лучше так
Код: c#
1.
public static string ToStringNormalize(this decimal value)


Это же для вывода надо. Чего там у decimal внутри в остальных случаях фиолетово. Так будет сгенерена только одна строка, и там где это надо, т.е. пойдет дальнейшую работу, а не сразу в мусор.
...
Рейтинг: 0 / 0
10.12.2015, 14:29
    #39124968
Ex_Soft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
decimal.ToString() trim trailing zeros
Dima TИМХУ лучше так
Код: c#
1.
public static string ToStringNormalize(this decimal value)


Это же для вывода надо. Чего там у decimal внутри в остальных случаях фиолетово. Так будет сгенерена только одна строка, и там где это надо, т.е. пойдет дальнейшую работу, а не сразу в мусор.
Дык в том-то и засада, что этот Decimal.ToString() юзают далее везде применительно к моей decimal property.

_________________
"Helo, word!" - 17 errors 56 warnings
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / decimal.ToString() trim trailing zeros / 11 сообщений из 11, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]