Utilizza virgola mobile o decimale per l’importo in dollari dell’applicazione contabile?

Stiamo riscrivendo il nostro sistema di contabilità legacy in VB.NET e SQL Server. Abbiamo creato un nuovo team di programmatori .NET / SQL per eseguire la riscrittura. La maggior parte del sistema è già completata con gli importi in dollari utilizzando Floats. Il linguaggio di sistema legacy, che ho programmato, non aveva un Float, quindi probabilmente avrei usato un decimale.

Qual è la tua raccomandazione?

Il tipo di dati Float o Decimal deve essere utilizzato per importi in dollari?

Quali sono alcuni dei pro e dei contro per entrambi?

One Con menzionato nella nostra mischia giornaliera era che bisogna stare attenti quando si calcola un importo che restituisce un risultato superiore a due posizioni decimali. Sembra che dovrai arrotondare l’importo a due posizioni decimali.

Un altro Con è tutte le visualizzazioni e gli importi stampati devono avere una dichiarazione di formato che mostra due posizioni decimali. Ho notato alcune volte che ciò non è stato fatto e le quantità non sembravano corrette. (cioè 10.2 o 10.2546)

Un professionista è che il Float occupa solo 8 byte su disco, dove il decimale occuperebbe 9 byte (decimale 12,2)

Il tipo di dati Float o Decimal deve essere utilizzato per importi in dollari?

La risposta è facile. Non galleggia mai. MAI !

I galleggianti erano secondo IEEE 754 sempre binari, solo il nuovo standard IEEE 754R definiva i formati decimali. Molte delle parti binarie frazionarie non possono mai eguagliare la rappresentazione decimale esatta. Qualsiasi numero binario può essere scritto come m / 2 ^ n (m, n interi positivi), qualsiasi numero decimale come m / (2 ^ n * 5 ^ n). Poiché i binari non hanno il fattore primo 5, tutti i numeri binari possono essere esattamente rappresentati da decimali, ma non viceversa.

0,3 = 3 / (2 ^ 1 * 5 ^ 1) = 0,3

0,3 = [0,25 / 0,5] [0,25 / 0,375] [0,25 / 3,15] [0,2825 / 3,15]

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

Quindi si finisce con un numero più alto o più basso del numero decimale dato. Sempre.

Perché importa? Arrotondamento. Arrotondamento normale significa 0,4 down, 5,9 su. Quindi importa se il risultato è 0.049999999999 …. o 0.0500000000 … Potresti sapere che significa 5 cent, ma il computer non lo sa e arrotonda 0,4999 … in basso (sbagliato) e 0,5000 .. . su (a destra). Dato che il risultato dei calcoli in virgola mobile contiene sempre piccoli termini di errore, la decisione è pura fortuna. Diventa imansible se si desidera una gestione decimale round-to-even con numeri binari.

Poco convinta ? Insisti sul fatto che nel tuo sistema di account tutto sia perfettamente ok? Attività e passività uguali? Ok, quindi prendi ciascuno dei numeri formattati di ciascuna voce, analizzali e sumli con un sistema decimale indipendente! Confrontalo con la sum formattata. Oops, c’è qualcosa che non va, non è vero?

Per quel calcolo, era richiesta estrema accuratezza e fedeltà (abbiamo usato il FLOAT di Oracle) in modo da poter registrare il “miliardesimo di un centesimo” accumulato.

Non aiuta contro questo errore. Perché tutte le persone automaticamente presumono che il computer sommi bene, praticamente nessuno controlla in modo indipendente.

Questa foto risponde:

foto1, C.O.

Questa è un’altra situazione: l’ uomo di Northampton ha ricevuto una lettera in cui si afferma che la sua casa sarebbe stata sequestrata se non avesse pagato zero dollari e zero centesimi!

foto2, C.O.

Per prima cosa dovresti leggere questo che ogni scienziato informatico dovrebbe sapere sull’aritmetica in virgola mobile . Quindi dovresti davvero prendere in considerazione l’uso di un certo tipo di pacchetto di numeri a virgola fissa / arbitraria (es. Java BigNum, modulo decimale di python) altrimenti ti troverai in un mondo di dolore. Quindi capire se è sufficiente utilizzare il tipo decimale SQL nativo.

Floats / doubles esistono (ed) per esporre il veloce x87 fp che ora è praticamente obsoleto. Non usarli se ti interessa la precisione dei calcoli e / o non compensare completamente i loro limiti.

Come ulteriore avviso, SQL Server e il framework .Net utilizzano un diverso algoritmo predefinito per l’arrotondamento. Assicurati di controllare il parametro MidPointRounding in Math.Round (). .Net framework utilizza l’algoritmo di Bankers per impostazione predefinita e SQL Server utilizza Symmetric Algorithmic Rounding. Dai un’occhiata all’articolo di Wikipedia qui

Chiedi ai tuoi contabili! Ti guarderanno male per aver usato il galleggiante. Come alcuni già pubblicati, usa SOLAMENTE il float se non ti interessa la precisione. Anche se sarei sempre contraria quando si tratta di soldi.

Nel software di contabilità NON è accettabile un galleggiante. Usa decimale con 4 punti decimali.

I punti in virgola mobile hanno numeri irrazionali inattesi.

Ad esempio non puoi memorizzare 1/3 come decimale, sarebbe 0.3333333333 … (e così via)

I float vengono effettivamente memorizzati come valore binario e potenza di 2 esponenti.

Quindi 1,5 viene memorizzato come 3 x 2 in -1 (o 3/2)

L’uso di questi esponenti di base-2 crea alcuni numeri irrazionali dispari, ad esempio:

Converti 1.1 in un float e poi riconvertilo di nuovo, il tuo risultato sarà qualcosa come: 1.0999999999989

Questo perché la rappresentazione binaria di 1.1 è in realtà 154811237190861 x 2 ^ -47, più di un doppio in grado di gestire.

Maggiori informazioni su questo problema sul mio blog , ma in fondo, per l’archiviazione, si sta meglio con i decimali.

Su Microsoft SQL server si ha il tipo di dati in money – questo di solito è il migliore per l’archiviazione finanziaria. È preciso a 4 posizioni decimali.

Per i calcoli si ha più di un problema – l’inaccuratezza è una piccola frazione, ma la metti in una funzione di potere e diventa rapidamente significativa.

Comunque i decimali non sono molto buoni per qualsiasi tipo di matematica – per esempio non esiste un supporto nativo per i poteri decimali.

Utilizzare il tipo decimale del server SQL.

Non usare denaro o galleggiare .

il denaro usa 4 cifre decimali, è più veloce dell’uso del decimale. BUT soffre di alcuni ovvi e alcuni problemi non così ovvi con l’arrotondamento ( vedi questo problema di connessione )

Quello che raccomanderei è usare numeri interi a 64 bit che memorizzano l’intero importo in centesimi.

Un po ‘di background qui ….

Nessun sistema numerico può gestire tutti i numeri reali con precisione. Tutti hanno i loro limiti e questo include sia il virgola mobile IEEE standard che i decimali con segno. Il punto mobile IEEE è più preciso per bit utilizzato, ma non importa qui.

I numeri finanziari sono basati su secoli di pratica di carta e penna, con convenzioni associate. Sono ragionevolmente precisi, ma, soprattutto, sono riproducibili. Due ragionieri che lavorano con vari numeri e tariffe dovrebbero venire con lo stesso numero. Qualsiasi spazio per discrepanza è spazio per frode.

Pertanto, per i calcoli finanziari, la risposta corretta è qualunque dia la stessa risposta di un CPA che è bravo in aritmetica. Questa è l’aritmetica decimale, non il punto mobile IEEE.

L’unica ragione per usare Float per soldi è se non ti importa delle risposte accurate.

I float non sono rappresentazioni esatte, sono possibili problemi di precisione, ad esempio quando si aggiungono valori molto grandi e molto piccoli. Ecco perché i tipi decimali sono raccomandati per la valuta, anche se il problema di precisione potrebbe essere sufficientemente raro.

Per chiarire, il tipo decimale 12,2 memorizzerà esattamente quelle 14 cifre, mentre il float non lo farà poiché utilizza internamente una rappresentazione binaria. Ad esempio, 0,01 non può essere rappresentato esattamente da un numero in virgola mobile – la rappresentazione più vicina è in realtà 0,0099999998

Per un sistema bancario che ho contribuito a sviluppare, ero responsabile per la parte “interessi maturati” del sistema. Ogni giorno, il mio codice calcolava quanti interessi erano stati accumulati (guadagni) sul saldo quel giorno.

Per quel calcolo, era richiesta estrema accuratezza e fedeltà (abbiamo usato il FLOAT di Oracle) in modo da poter registrare il “miliardesimo di un centesimo” accumulato.

Quando si è trattato di “capitalizzare” l’interesse (cioè di ripagare l’interesse sul proprio conto), l’importo è stato arrotondato al centesimo. Il tipo di dati per i saldi dell’account era di due cifre decimali. (In effetti era più complicato in quanto era un sistema multivaluta che poteva funzionare in molti decimali – ma abbiamo sempre arrotondato il “penny” di quella valuta). Sì, là dove “le frazioni” di perdita e guadagno, ma quando le cifre dei computer sono state attualizzate (soldi pagati o pagati) erano sempre valori di denaro reali.

Questo ha soddisfatto i ragionieri, i revisori dei conti e i tester.

Quindi, controlla con i tuoi clienti. Ti diranno le loro regole e pratiche bancarie / contabili.

Un’altra cosa di cui dovresti essere a conoscenza nei sistemi di contabilità è che nessuno dovrebbe avere accesso diretto ai tavoli. Ciò significa che tutti gli accessi al sistema di contabilità devono avvenire tramite stored procs. Questo è prevenire le frodi non solo gli attacchi SQl. Un utente interno che vuole commettere una frode non dovrebbe avere la possibilità di modificare direttamente i dati nelle tabelle del database, mai. Questo è un controllo interno critico sul tuo sistema. Vuoi veramente che un dipendente scontento vada nel backend del tuo database e inizi a scriverli? O hide che hanno approvato una spesa per un venditore non autorizzato quando non hanno l’authorization di approvazione? Solo due persone nella tua intera organizzazione dovrebbero essere in grado di accedere direttamente ai dati nel tuo database finanziario, il tuo dba e il suo backup. Se hai molti dbas, solo due di questi dovrebbero avere questo accesso.

Lo dico perché se i tuoi programmatori usavano fluttuare in un sistema di contabilità, probabilmente non conoscevano completamente l’idea dei controlli interni e non li consideravano nei loro sforzi di programmazione.

Ancora meglio che usare i decimali sta usando semplicemente vecchi interi (o forse una sorta di bigint). In questo modo hai sempre la massima precisione ansible, ma la precisione può essere specificata. Ad esempio il numero 100 potrebbe significare 1.00 , che è formattato in questo modo:

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

Se ti piace avere maggiore precisione, puoi modificare il valore 100 in un valore più grande, come: 10 ^ n, dove n è il numero di decimali.

Puoi sempre scrivere qualcosa come un tipo di denaro per .Net.

Date un’occhiata a questo articolo: Un tipo di soldi per il CLR – L’autore ha fatto un lavoro eccellente a mio parere.

Stavo usando il tipo di denaro di SQL per la memorizzazione di valori monetari. Recentemente, ho dovuto lavorare con un numero di sistemi di pagamento online e ho notato che alcuni di loro usano numeri interi per memorizzare valori monetari. Nei miei progetti attuali e nuovi ho iniziato a utilizzare numeri interi e sono abbastanza soddisfatto di questa soluzione.

Tra le 100 frazioni n / 100, dove n è un numero naturale tale che 0 <= n e n <100, solo quattro possono essere rappresentati come numeri in virgola mobile. Dai un'occhiata all'output di questo programma C:

 #include  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; } 

Hai preso in considerazione l’utilizzo del tipo di dati monetari per archiviare importi in dollari?

Per quanto riguarda il Con, quel decimale occupa un altro byte, direi che non gli interessa. In 1 milione di righe utilizzerai solo 1 altro MB e l’archiviazione è molto economica in questi giorni.

Qualunque cosa tu faccia, devi fare attenzione agli errori di arrotondamento. Calcola usando un grado di precisione maggiore di quello visualizzato.

Probabilmente vorrai utilizzare una qualche forma di rappresentazione a virgola fissa per i valori di valuta. Dovrai anche indagare sull’arrotondamento del Banker (noto anche come “round half even”.) Evita i bias che esistono nel solito metodo “round half up”.

I tuoi contabili vorranno controllare come arrotondare. Usare float significa che sarai costantemente arrotondato, di solito con un’istruzione di tipo FORMAT (), che non è il modo in cui vuoi farlo (usa invece floor / ceiling).

Hai dei tipi di dati di valuta (denaro, smallmoney), che dovrebbero essere usati al posto di float o reali. La memorizzazione dei decimali (12,2) eliminerà i tuoi arrotondamenti, ma li eliminerà anche durante i passaggi intermedi – che in realtà non è ciò che vorrai in un’applicazione finanziaria.

Usa sempre decimale. Float ti fornirà valori inaccurati a causa di problemi di arrotondamento.

I numeri in virgola mobile possono rappresentare solo numeri che sono una sum di multipli negativi della base – per il punto in virgola mobile, naturalmente, questo è due.

Esistono solo quattro frazioni decimali rappresentabili precisamente nel punto mobile binario: 0, 0,25, 0,5 e 0,75. Tutto il resto è un’approssimazione, nello stesso modo in cui 0.3333 … è un’approssimazione per 1/3 nell’aritmetica decimale.

Il punto mobile è una buona scelta per i calcoli in cui la scala del risultato è l’importante. È una ctriggers scelta in cui stai cercando di essere accurato con un numero di cifre decimali.

Questo è un articolo eccellente che descrive quando usare float e decimal . Float memorizza un valore approssimativo e decimale memorizza un valore esatto.

In breve, valori esatti come il denaro dovrebbero usare decimali e valori approssimativi come le misurazioni scientifiche dovrebbero usare il float.

Ecco un esempio interessante che mostra che sia il float che il decimale sono in grado di perdere precisione. Quando si aggiunge un numero che non è un numero intero e quindi si sottrae lo stesso numero a virgola mobile, si perde la precisione mentre il decimale non lo è:

  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 

Quando si moltiplica un numero intero non intero e si divide per lo stesso numero, i decimali perdono precisione mentre i float no.

 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