Użyj Float lub Decimal do obliczania kwoty dolara aplikacji?

Przepisujemy nasz dotychczasowy system księgowy w VB.NET i SQL Server. Sprowadziliśmy nowy zespół programistów. NET/ SQL do przepisania. Większość systemu jest już zakończona kwotami dolarowymi za pomocą pływaków. Starszy język systemowy, w którym zaprogramowałem, nie miał Float ' a, więc prawdopodobnie użyłbym dziesiętnego.

Jaka jest twoja rekomendacja?

Czy dla wartości dolara należy stosować typ danych Float lub Decimal?

Jakie są wady i zalety albo?

Jednym z oszustw wymienionych w naszym codziennym scrum było to, że trzeba być ostrożnym przy obliczaniu kwoty, która zwraca wynik przekraczający dwa miejsca po przecinku. Wygląda na to, że będziesz musiał zaokrąglić kwotę do dwóch miejsc po przecinku.

Kolejnym przekrętem jest to, że wszystkie wyświetlacze i wydrukowane kwoty muszą mieć instrukcję Format, która pokazuje dwa miejsca po przecinku. Zauważyłem kilka razy, gdzie to nie zostało zrobione i kwoty nie wyglądają poprawnie. (tj. 10.2 lub 10.2546)

A pro is zmiennoprzecinkowy zajmuje tylko 8 bajtów na dysku, gdzie dziesiętny zajmuje 9 bajtów (Decimal 12,2)

Author: Nakilon, 2008-09-15

24 answers

Czy dla kwot w dolarach należy stosować typ danych Float lub Decimal?

Odpowiedź jest prosta. Nigdy nie pływa. Nigdy !

Pływaki były zgodne z IEEE 754 zawsze binarne, tylko nowy standard IEEE 754R zdefiniowane formaty dziesiętne. Wiele ułamkowych części binarnych nigdy nie może równać się dokładnemu dziesiętnemu reprezentacyjne. Każda liczba binarna może być zapisana jako m/2^N (M, N dodatnie liczby całkowite), każda liczba dziesiętna jako m / (2^N*5^N). Ponieważ binaria nie mają współczynnik pierwszy 5, wszystkie liczby binarne mogą być dokładnie reprezentowane przez liczby dziesiętne, ale nie odwrotnie.

0.3 = 3/(2^1 * 5^1) = 0.3

0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]

     1/4       1/8     1/16       1/32

Więc kończysz z liczbą wyższą lub niższą od podanej liczby dziesiętnej. Zawsze.

Jakie to ma znaczenie ? Zaokrąglanie. Zwykłe zaokrąglenie oznacza 0..4 w dół, 5..9 w górę. Więc to ma znaczenie, jeśli wynik jest albo 0.049999999999.... lub 0.0500000000... Możesz wiedzieć to znaczy 5 centów, ale komputer tego nie wie i wynosi 0,4999... w dół (źle) i 0,5 tys... w górę (z prawej). Biorąc pod uwagę, że wynik obliczeń zmiennoprzecinkowych zawsze zawiera małe terminy błędów, decyzja jest czystym szczęściem. Staje się beznadziejne, jeśli chcesz obsługa liczb binarnych.

Nieprzekonany ? Twierdzisz, że w Twoim systemie kont wszystko jest w porządku ? Aktywa i pasywa równe? OK, następnie weź każdą z podanych sformatowanych liczb każdego wprowadź, przeanalizuj je i zsumuj z niezależnym systemem dziesiętnym ! Porównaj to z sformatowaną sumą. UPS, coś jest nie tak, prawda ?

Do tego obliczenia wymagana była ekstremalna dokładność i wierność (użyliśmy Oracle ' a FLOAT), abyśmy mogli nagrać" miliardowe grosze " naliczane.

Nie pomaga na ten błąd. Ponieważ wszyscy ludzie automatycznie zakładają, że komputer dobrze sumuje, praktycznie nikt nie sprawdza niezależnie.

 103
Author: TSK,
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
2012-05-14 20:43:03
 40
Author: Nakilon,
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-02-20 06:08:03

Najpierw powinieneś przeczytać to Co każdy informatyk powinien wiedzieć o arytmetyce zmiennoprzecinkowej. Następnie powinieneś naprawdę rozważyć użycie jakiegoś typu pakietu o stałym punkcie / arbitralnej precyzji (np. java BigNum, Python decimal module), w przeciwnym razie będziesz w świecie bólu. Następnie dowiedz się, czy użycie natywnego typu dziesiętnego SQL jest wystarczające.

Floats / doubles istnieją (ed), aby ujawnić szybki fp x87, który jest obecnie dość przestarzały. Nie używaj ich, jeśli dbaj o dokładność obliczeń i / lub nie kompensuj w pełni ich ograniczeń.

 20
Author: Rich Schuler,
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-09-15 08:04:29

Jako dodatkowe ostrzeżenie, SQL Server i. Net framework używają innego domyślnego algorytmu zaokrąglania. Upewnij się, że sprawdzasz parametr MidPointRounding w matematyce.Rundzie (). . NET framework domyślnie wykorzystuje algorytm Bankersa, a SQL Server wykorzystuje algorytmiczne Zaokrąglanie symetryczne. Zobacz artykuł w Wikipedii tutaj

 10
Author: Darrel Miller,
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-09-15 13:21:45

Zapytaj księgowych! Będą marszczyć brwi na Tobie za używanie float. Podobnie jak ktoś wcześniej, używaj float tylko wtedy, gdy nie zależy ci na dokładności. Chociaż zawsze byłbym przeciwny, jeśli chodzi o pieniądze.

W oprogramowaniu księgowym jest niedopuszczalny float. Użyj dziesiętnego z 4 punktami dziesiętnymi.

 7
Author: ,
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-09-15 05:22:12

Punkty zmiennoprzecinkowe mają nieoczekiwane liczby irracjonalne.

Na przykład nie można zapisać 1/3 jako dziesiętny, będzie to 0.3333333333... (i tak dalej)

Pływaki są w rzeczywistości przechowywane jako wartość binarna i potęga wykładnika 2.

Tak więc 1,5 jest zapisywane jako 3 x 2 do -1 (lub 3/2)

Używając tych wykładników Bazy-2 tworzymy kilka nieparzystych liczb irracjonalnych, na przykład:

Przekonwertuj 1.1 na zmiennoprzecinkowy, a następnie przekonwertuj go z powrotem, Twój wynik będzie podobny: 1.0999999999989

Dzieje się tak dlatego, że reprezentacja binarna 1.1 jest w rzeczywistości 154811237190861 x 2^-47, więcej niż Podwójna może obsłużyć.

Więcej o tym numerze na moim blogu , ale zasadniczo, do przechowywania, lepiej będzie z dziesiętnikami.

Na Microsoft SQL server masz typ danych money - zwykle jest to najlepsze do przechowywania danych finansowych. Jest dokładna do 4 miejsc po przecinku.

Do obliczeń masz większy problem - niedokładność jest maleńka ułamek, ale umieść go w funkcji mocy i szybko staje się znacząca.

Jednak liczby dziesiętne nie są zbyt dobre dla żadnego rodzaju matematyki - na przykład nie ma natywnego wsparcia dla potęg dziesiętnych.

 6
Author: Keith,
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-09-15 09:59:38

Użyj typu SQL server decimal .

Nie używaj Pieniędzy ani float .

Pieniądz używa 4 miejsc po przecinku, jest szybszy niż dziesiętny , ale cierpi na pewne oczywiste i nie tak oczywiste problemy z zaokrągleniem (zobacz ten problem connect)

 5
Author: Mitch Wheat,
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-03 01:00:38

Polecam użycie 64-bitowych liczb całkowitych, które przechowują całość w centach.

 5
Author: Joshua,
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-03 01:08:31

Trochę tła tutaj....

Żaden system liczbowy nie jest w stanie dokładnie obsłużyć wszystkich liczb rzeczywistych. Wszystkie mają swoje ograniczenia, a to obejmuje zarówno standard IEEE zmiennoprzecinkowy i podpisane dziesiętne. Zmiennoprzecinkowa IEEE jest dokładniejsza dla każdego bitu, ale nie ma to tutaj znaczenia.

Liczby finansowe są oparte na wielowiekowej praktyce papierowej i piórkowej, z powiązanymi konwencjami. Są dość dokładne, ale co ważniejsze, powtarzalne. Dwóch księgowych praca z różnymi numerami i stawkami powinna pochodzić z tego samego numeru. Każde miejsce na rozbieżności jest miejscem na oszustwo.

Dlatego, dla obliczeń finansowych, właściwą odpowiedzią jest to, co daje taką samą odpowiedź jak CPA, który jest dobry w arytmetyce. Jest to arytmetyka dziesiętna, a nie zmiennoprzecinkowa IEEE.

 5
Author: David Thornley,
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
2009-02-09 16:42:15

Jedynym powodem, aby używać Float dla pieniędzy jest, jeśli nie dbają o dokładne odpowiedzi.

 4
Author: David Singer,
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-09-15 05:00:04

Pływaki nie są dokładnymi reprezentacjami, możliwe są problemy z precyzją, na przykład przy dodawaniu bardzo dużych i bardzo małych wartości. Dlatego typy dziesiętne są zalecane dla waluty, nawet jeśli kwestia precyzji może być wystarczająco rzadka.

Dla wyjaśnienia, Typ decimal 12,2 będzie przechowywał dokładnie te 14 cyfr, podczas gdy float nie będzie, ponieważ używa wewnętrznej reprezentacji binarnej. Na przykład 0.01 nie może być dokładnie reprezentowane przez liczbę zmiennoprzecinkową-najbliższą reprezentacja jest rzeczywiście 0.0099999998

 4
Author: Niall,
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-09-15 05:10:58

W przypadku systemu bankowego, który pomogłem rozwinąć, byłem odpowiedzialny za część systemu "naliczania odsetek". Każdego dnia mój kod obliczał, ile odsetek zostało naliczonych (zarobionych) na saldzie tego dnia.

Do tego obliczenia wymagana była ekstremalna dokładność i wierność (użyliśmy FLOAT Oracle ' a), abyśmy mogli zarejestrować naliczane "miliardowe grosze".

Jeśli chodzi o "kapitalizację" odsetek (tj. spłacając odsetki z powrotem na konto) kwota była zaokrąglone do grosza. Typ danych dla sald kont wynosił dwa miejsca po przecinku. (Faktycznie byĹ 'o to bardziej skomplikowane jako ĹĽe byĹ' to wielowalutowy system mogÄ ... cy dziaĹ ' aÄ ‡ w wielu miejscach po przecinku-jednak zawsze zaokrÄ ... glaliĺ "my siÄ ™ do" grosza " tej waluty). Tak - tam, gdzie "ułamki" straty i zysku, ale kiedy dane komputerowe były aktualizowane (pieniądze wypłacane lub wypłacane) to zawsze były prawdziwe wartości pieniędzy.

To zadowoliło księgowych, audytorów i testerów.

Więc sprawdź z twoi klienci. Powiedzą Ci swoje zasady i praktyki bankowe/księgowe.

 4
Author: Guy,
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-09-15 10:42:26

Kolejną rzeczą, o której należy pamiętać w systemach księgowych, jest to, że nikt nie powinien mieć bezpośredniego dostępu do tabel. Oznacza to, że cały dostęp do systemu księgowego musi odbywać się poprzez przechowywane procs. Jest to zapobieganie oszustwom, a nie tylko atakom SQL injection. Użytkownik wewnętrzny, który chce popełnić oszustwo, nie powinien mieć możliwości bezpośredniej zmiany danych w tabelach bazy danych, nigdy. To jest wewnętrzna kontrola critcal w Twoim systemie. Czy naprawdę chcesz, aby jakiś niezadowolony pracownik poszedł do zaplecza twoja baza danych i niech zacznie wypisywać czeki? Lub ukryć, że zatwierdzili wydatek nieautoryzowanemu sprzedawcy, gdy nie mają uprawnień do zatwierdzenia? Tylko dwie osoby w całej organizacji powinny mieć bezpośredni dostęp do danych w Twojej bazie danych finansowych, twojego dba i jego kopii zapasowej. Jeśli masz wiele dba, tylko dwa z nich powinny mieć ten dostęp.

Wspominam o tym, ponieważ jeśli twoi Programiści używali float w systemie księgowym, prawdopodobnie są całkowicie nieznani idei kontroli wewnętrznych i nie uwzględnili ich w swoich działaniach programistycznych.

 4
Author: HLGEM,
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
2013-06-30 07:51:13

Nawet lepsze niż używanie dziesiętnych jest używanie zwykłych starych liczb całkowitych (a może jakiegoś biginta). W ten sposób zawsze masz najwyższą możliwą dokładność, ale precyzję można określić. Na przykład liczba {[1] } może oznaczać 1.00, która jest sformatowana tak:

int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);

Jeśli chcesz mieć większą precyzję, możesz zmienić 100 na większą wartość, np.: 10 ^ n, gdzie n to liczba miejsc po przecinku.

 3
Author: Peter Stuifzand,
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-09-16 10:08:31

Zawsze możesz napisać coś takiego jak typ pieniędzy dla .Net.

Spójrz na ten artykuł: Typ pieniędzy na CLR - autor wykonał świetną pracę moim zdaniem.

 2
Author: Tomer Pintel,
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-09-21 06:12:24

Używałem SQL typu pieniądze do przechowywania wartości pieniężnych. Ostatnio musiałem pracować z wieloma systemami płatności online i zauważyłem, że niektóre z nich używają liczb całkowitych do przechowywania wartości pieniężnych. W moich obecnych i nowych projektach zacząłem używać liczb całkowitych i jestem całkiem zadowolony z tego rozwiązania.

 2
Author: George,
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
2009-02-09 14:09:47

Ze 100 ułamków n/100, gdzie n jest liczbą naturalną taką, że 0

#include <stdio.h>

int main()
{
    printf("Mapping 100 numbers between 0 and 1 ");
    printf("to their hexadecimal exponential form (HEF).\n");
    printf("Most of them do not equal their HEFs. That means ");
    printf("that their representations as floats ");
    printf("differ from their actual values.\n");
    double f = 0.01;
    int i;
    for (i = 0; i < 100; i++) {
        printf("%1.2f -> %a\n",f*i,f*i);
    }
    printf("Printing 128 'float-compatible' numbers ");
    printf("together with their HEFs for comparison.\n");
    f = 0x1p-7; // ==0.0071825
    for (i = 0; i < 0x80; i++) {
        printf("%1.7f -> %a\n",f*i,f*i);
    }
    return 0;
}
 2
Author: Lars Bohl,
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
2010-10-23 20:17:15

Czy rozważałeś użycie typu money-data do przechowywania kwot dolara?

Jeśli chodzi o przekręt, który zajmuje jeszcze jeden bajt, powiedziałbym, że nie obchodzi mnie to. W 1 milionie wierszy użyjesz tylko 1 MB więcej, a pamięć jest bardzo tania w dzisiejszych czasach.

 1
Author: Espo,
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-09-15 04:59:07

Cokolwiek robisz, musisz uważać na błędy zaokrąglania. Oblicz przy użyciu większej precyzji niż wyświetlana w.

 1
Author: 1800 INFORMATION,
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-09-15 04:59:18

Prawdopodobnie będziesz chciał użyć jakiejś formy reprezentacji stałych punktów dla wartości waluty. Będziesz również chciał zbadać zaokrąglenia bankiera (znany również jako "okrągła połowa parzysta".) Unika uprzedzeń, które istnieją w zwykłej metodzie "round half up".

 1
Author: user6931,
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-09-15 13:19:10

Twoi księgowi będą chcieli kontrolować, jak kręcisz. Użycie float oznacza, że będziesz stale zaokrąglać, zwykle za pomocą polecenia FORMAT() type, co nie jest sposobem ,w jaki chcesz to zrobić (zamiast tego użyj podłogi / sufitu).

Masz typy danych walut (money, smallmoney), które powinny być używane zamiast float lub real. Zapis dziesiętny (12,2) wyeliminuje Twoje zaokrąglenia, ale także wyeliminuje je podczas etapów pośrednich - co naprawdę nie jest tym, czego w ogóle chcesz w wniosek finansowy.

 0
Author: David T. Macknet,
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-09-15 12:48:31

Zawsze używaj dziesiętnych. Float daje niedokładne wartości z powodu problemów z zaokrągleniem.

 0
Author: ,
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-09-15 12:50:46

Liczby zmiennoprzecinkowe mogą tylko reprezentować liczby, które są sumą ujemnych wielokrotności bazy - dla binarnych liczb zmiennoprzecinkowych, oczywiście, to dwa.

Istnieją tylko cztery ułamki dziesiętne reprezentowalne dokładnie w binarnym punkcie zmiennoprzecinkowym: 0, 0.25, 0.5 i 0.75. Wszystko inne jest przybliżeniem, w taki sam sposób, jak 0,3333... jest przybliżeniem 1/3 w arytmetyce dziesiętnej.

Zmiennoprzecinkowy jest dobrym wyborem do obliczeń, w których skala wyniku to jest ważne. To zły wybór, gdy starasz się być dokładnym do pewnej liczby miejsc po przecinku.

 0
Author: Mike Dimmick,
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-09-15 12:55:35

Jest to doskonały artykuł opisujący Kiedy używać float i decimal . Float przechowuje przybliżoną wartość, a decimal - dokładną.

Podsumowując, dokładne wartości, takie jak pieniądze, powinny używać dziesiętnych, a przybliżone wartości, takie jak pomiary naukowe, powinny używać float.

Oto ciekawy przykład, który pokazuje, że zarówno float, jak i decimal są w stanie stracić precyzję. Przy dodawaniu liczby, która nie jest liczbą całkowitą, a następnie odejmowaniu tej samej liczby powoduje utratę precyzji, podczas gdy liczba dziesiętna nie:

    DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float; 
    SET @Float1 = 54; 
    SET @Float2 = 3.1; 
    SET @Float3 = 0 + @Float1 + @Float2; 
    SELECT @Float3 - @Float1 - @Float2 AS "Should be 0";

Should be 0 
---------------------- 
1.13797860024079E-15

Mnożąc liczbę całkowitą i dzieląc przez tę samą liczbę, liczby dziesiętne tracą precyzję, podczas gdy pływaki nie.

DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4); 
SET @Fixed1 = 54; 
SET @Fixed2 = 0.03; 
SET @Fixed3 = 1 * @Fixed1 / @Fixed2; 
SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1";

Should be 1 
--------------------------------------- 
0.99999999999999900
 0
Author: BrokeMyLegBiking,
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
2010-10-23 01:58:43