2012-01-02 15 views
15

Podczas rozmowy na IRC, ktoś zauważył, co następuje:Czy zapisuje dokładność w pamięci dziesiętnej z parsowanego ciągu w C#? Jakie są konsekwencje?

decimal.Parse("1.0000").ToString() // 1.0000 
decimal.Parse("1.00").ToString() // 1.00 

Jak/dlaczego typ decimal zachować precyzję (lub raczej cyfr znaczących) w taki sposób? Miałem wrażenie, że te dwie wartości są równe, a nie odrębne.

Rodzi to również dalsze pytania:

  • Jak jest liczba cyfr znaczących postanowił podczas operacji matematycznych?
  • Czy liczba ważnych cyfr zostanie zatrzymana podczas serializacji?
  • Czy obecna kultura wpływa na sposób jej obsługi?
+10

Jeśli chodzi o [jak] (http://msdn.microsoft.com/en-us/library/system.decimal.aspx), robi to: "binarna reprezentacja wartości dziesiętnej składa się ze znaku 1-bitowego, 96-bitowa liczba całkowita i współczynnik skalujący używany do dzielenia 96-bitowej liczby całkowitej i określania jej części jako ułamka dziesiętnego. Współczynnik skalowania jest niejawnie liczbą 10, podniesioną do wykładnika w zakresie od 0 do 28. " - współczynnik skalowania jest komponentem dyskretnym. –

+0

@pst - Więc czy współczynnik skalujący jest wyłącznie odpowiedzialny za ważne liczby? tj. '1.0000' to' 10000/10^4' i '1.00' to' 100/10^2' – Polynomial

Odpowiedz

9

Jaka jest liczba znaczących cyfr zadeklarowanych podczas operacji matematycznych?

ta jest określona w ECMA-334 C# 4 specyfikacji 11.1.7 str.112

ułamek dziesiętny jest reprezentowany jako liczba całkowita skalowany przez potęgę dziesiątki. W przypadku liczb dziesiętnych z wartością bezwzględną mniejszą niż 1,0 m, wartość ta jest równa co najmniej 28 miejscu po przecinku. W przypadku wartości dziesiętnych z wartością bezwzględną większą niż lub równą 1,0 m, wartość jest dokładna co najmniej 28 cyfr.

Czy liczba ważnych cyfr zostanie zatrzymana podczas serializacji?

Tak to robi, z seriallization wartość, a jego precyzja nie zmienia

[Serializable] 
public class Foo 
{ 
    public decimal Value; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     decimal d1 = decimal.Parse("1.0000"); 
     decimal d2 = decimal.Parse("1.00"); 

     Debug.Assert(d1 ==d2); 

     var foo1 = new Foo() {Value = d1}; 
     var foo2 = new Foo() {Value = d2}; 

     IFormatter formatter = new BinaryFormatter(); 
     Stream stream = new FileStream("data.bin", FileMode.Create, FileAccess.Write, FileShare.None); 
     formatter.Serialize(stream, d1); 
     stream.Close(); 

     formatter = new BinaryFormatter(); 
     stream = new FileStream("data.bin", FileMode.Open, FileAccess.Read, FileShare.Read); 
     decimal deserializedD1 = (decimal)formatter.Deserialize(stream); 
     stream.Close(); 

     Debug.Assert(d1 == deserializedD1); 

     Console.WriteLine(d1); //1.0000 
     Console.WriteLine(d2); //1.00 
     Console.WriteLine(deserializedD1); //1.0000 

     Console.Read(); 
    } 
} 

Czy obecna kultura wpływa na sposób, w jaki to obchodzić?

Obecna kultura wpływa tylko na to, jak można sparsować dziesiętne z ciągu, na przykład może obsłużyć "." lub ',' jako symbol kropki dziesiętnej specyficznej dla kultury lub symbol waluty, jeśli ją podasz, np. "123.4500 £". Kultura nie zmienia sposobu, w jaki obiekt jest przechowywany wewnętrznie i nie wpływa na jego precyzję.

Wewnętrznie, decimal has mantysa, wykładnik i znak, więc nie ma miejsca na nic innego.

+0

Doskonała odpowiedź! Dziękuję Ci bardzo :) – Polynomial

1

decimal składa się z 96-bitowej liczby całkowitej i współczynnika skalowania (liczba miejsc po przecinku), która mieści się w zakresie od 0 do 28. W ten sposób:

  • 1,000 staje 1000 o współczynniku skalowania 3.
+0

Jak wpływa na współczynnik skalowania operacje matematyczne i serializacja? – Polynomial

+0

Współczynnik skalowania jest zawarty w zseryjnej postaci wartości dziesiętnej, uzyskanej po wywołaniu metody GetBits. Ponadto, jak określono w dokumentacji, "[zerowe] zerówki nie wpływają na wartość liczby dziesiętnej w operacjach arytmetycznych lub porównawczych." –

1

Ponadto odpowiedzieć widzę tutaj, chciałbym osobiście dodać notatkę boczną:

zawsze podczas manipulacji utrwalania ze zmiennym/dziesiętny/numery podwójne rozważyć culture jesteście, albo jesteś zamierzam zapisać. Kod taki jak tutaj jest napisany, jest pierwszym, ale definitywnym przejściem do kompletnej bałaganu i niezaakceptowania architektury non.

Użyj Decimal.Parse (String, IFormatProvider).

Moim zdaniem, te metody (Parse From/To), że brak parametru Culture muszą zostać usunięte z biblioteki, aby zmusić programistę do przemyślenia tego bardzo ważnego aspektu.

Powiązane problemy