Czy sobowtór naprawdę nie nadaje się do pieniędzy?

Zawsze mówię w c# zmienna typu double nie nadaje się do pieniędzy. Wszystkie dziwne rzeczy mogą się zdarzyć. Ale nie mogę stworzyć przykładu, aby zademonstrować niektóre z tych problemów. Czy ktoś może podać taki przykład?

(edit; ten post został pierwotnie oznaczony jako C#; niektóre odpowiedzi odnoszą się do konkretnych szczegółów decimal, co oznacza System.Decimal).

(edit 2: dokładnie prosiłem o jakiś kod c#, więc nie sądzę, że jest to tylko język agnostyczny)

Author: doekman, 2008-11-25

8 answers

Bardzo, bardzo nieodpowiednie. Użyj dziesiętnego.

double x = 3.65, y = 0.05, z = 3.7;
Console.WriteLine((x + y) == z); // false

(przykład ze strony Jona tutaj - zalecana lektura; - P)

 116
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-07 16:08:56

Otrzymasz dziwne błędy skutecznie spowodowane przez zaokrąglenie. Ponadto porównania z dokładnymi wartościami są niezwykle trudne - zwykle trzeba zastosować jakiś epsilon, aby sprawdzić, czy rzeczywista wartość jest "blisko" konkretnego.

Oto konkretny przykład:
using System;

class Test
{
    static void Main()
    {
        double x = 0.1;
        double y = x + x + x;
        Console.WriteLine(y == 0.3); // Prints False
    }
}
 34
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-16 13:45:35

Tak, to nieodpowiednie.

Jeśli dobrze pamiętam double ma około 17 cyfr znaczących, więc zwykle błędy zaokrąglania będą miały miejsce daleko za punktem dziesiętnym. Większość oprogramowania finansowego używa 4 miejsc po przecinku za punktem dziesiętnym, co pozostawia 13 miejsc po przecinku do pracy, więc maksymalna liczba, z którą można pracować dla pojedynczych operacji, jest nadal znacznie wyższa niż dług narodowy USA. Ale błędy zaokrąglania będą się sumować z czasem. Jeśli twoje oprogramowanie będzie działać przez długi czas, w końcu zacznij tracić centy. Niektóre operacje pogorszą sprawę. Na przykład dodanie dużych ilości do małych ilości spowoduje znaczną utratę precyzji.

Potrzebujesz stałych typów danych dla operacji pieniężnych, większość ludzi nie ma nic przeciwko, jeśli stracisz centa tu i tam, ale księgowi nie są jak większość ludzi..

Edit
Zgodnie z tą stroną http://msdn.microsoft.com/en-us/library/678hzkk9.aspx sobowtóry faktycznie mają od 15 do 16 cyfr znaczących zamiast 17.

@Jon Skeet dziesiętny jest bardziej odpowiedni niż podwójny ze względu na większą precyzję, 28 lub 29 znaczących miejsc po przecinku. Oznacza to mniejsze prawdopodobieństwo, że nagromadzone błędy zaokrąglania staną się znaczące. Fixed point datatypes (czyli liczby całkowite, które reprezentują centów lub 100th centa jak widziałem używane) jak Boojum wspomina są rzeczywiście lepiej dopasowane.

 7
Author: Mendelt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-25 12:24:20

Ponieważ decimal używa współczynnika skalowania wielokrotności 10, liczby takie jak 0,1 mogą być reprezentowane dokładnie. W istocie, Typ dziesiętny reprezentuje to jako 1 / 10 ^ 1, podczas gdy double reprezentowałby to jako 104857 / 2 ^ 20 (w rzeczywistości byłoby to bardziej naprawdę-duża-liczba / 2 ^ 1023).

A decimal może dokładnie reprezentować dowolną wartość bazową 10 z maksymalnie 28/29 znaczącymi cyframi(np. A double nie mogę.

 5
Author: Richard Poole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-25 09:28:30

Rozumiem, że większość systemów finansowych wyraża walutę za pomocą liczb całkowitych - tj. licząc wszystko w centach.

IEEE double precision faktycznie Może reprezentować wszystkie liczby całkowite dokładnie w zakresie od -2^53 do +2^53. (Hacker ' s Delight, pg. 262) jeśli używasz tylko dodawania, odejmowania i mnożenia i zachowujesz wszystko dla liczb całkowitych w tym zakresie, nie powinieneś widzieć utraty precyzji. Byłbym bardzo ostrożny z podziałem lub bardziej złożonymi operacjami, jednak.

 4
Author: Boojum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-25 09:19:45

Używanie podwójnego, gdy nie wiesz, co robisz, jest nieodpowiednie.

"podwójny" może reprezentować kwotę biliona dolarów z błędem 1/90 centa. Dzięki temu uzyskasz bardzo precyzyjne wyniki. Chcesz obliczyć, ile kosztuje wysłanie człowieka na Marsa i odzyskanie go żywego? podwójne wystarczy.

Ale w przypadku pieniędzy często istnieją bardzo szczegółowe zasady mówiące, że pewne obliczenia muszą dać określony wynik, a nie inne. Jeśli obliczysz kwotę, która jest bardzo bardzo bardzo blisko $98.135 wtedy często będzie zasada, która określa, czy wynik powinien być $ 98.14 lub $ 98.13 i {5]} musi przestrzegać tej zasady i uzyskać wynik, który jest wymagany.

W zależności od tego, gdzie mieszkasz, używanie 64-bitowych liczb całkowitych do reprezentowania centów, groszy lub kopiejek lub jakiejkolwiek najmniejszej jednostki w Twoim kraju zwykle działa dobrze. Na przykład 64-bitowe liczby całkowite reprezentujące centy mogą reprezentować wartości do 92,223 bilionów dolarów. 32 bit liczby całkowite są zwykle nieodpowiednie.

 3
Author: gnasher729,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-23 12:20:02

No double zawsze będzie miał błędy zaokrąglania, użyj "decimal" jeśli jesteś na. Net ...

 0
Author: Thomas Hansen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-25 12:56:18

W rzeczywistości zmiennoprzecinkowy double doskonale nadaje się do reprezentowania kwot pieniężnych, o ile wybierzesz odpowiednią jednostkę.

Zobacz http://www.idinews.com/moneyRep.html

Tak jest punkt stały Długi . Albo zużywa 8 bajtów, z pewnością lepiej niż 16 zużywanych przez element dziesiętny .

To, czy coś działa (tj. daje oczekiwany i poprawny wynik), nie jest sprawą ani głosowania, ani indywidualnych preferencji. A technika albo działa, albo nie.

 -4
Author: Conrad Weisert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-21 15:45:23