Domanda

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

Qual è il tuo consiglio?

È opportuno utilizzare il tipo di dati Float o Decimal per gli importi in dollari?

Quali sono i pro e i contro di entrambi?

Uno svantaggio menzionato nel nostro daily scrum è che devi stare attento quando calcoli un importo che restituisce un risultato superiore a due posizioni decimali.Sembra che dovrai arrotondare l'importo a due posizioni decimali.

Un altro svantaggio è che tutti gli importi visualizzati e stampati devono avere un'istruzione di formato che mostri due posizioni decimali.Ho notato alcune volte che ciò non è stato fatto e gli importi non sembravano corretti.(cioè.10.2 o 10.2546)

Un vantaggio è che il Float occupa solo 8 byte sul disco mentre il Decimal occuperebbe 9 byte (Decimal 12,2)

È stato utile?

Soluzione

È opportuno utilizzare il tipo di dati Float o Decimal per gli importi in dollari?

La risposta è semplice.Non galleggia mai. MAI !

I galleggianti erano secondo IEEE754 sempre binario, solo il nuovo standard IEEE754R formati decimali definiti.Molte delle parti binarie frazionarie non possono mai eguagliare l'esatta rappresentazione decimale.
Qualsiasi numero binario può essere scritto come m/2^n (m, n interi positivi), qualsiasi numero decimale come m/(2^n*5^n).
Poiché ai binari manca il numero primo factor 5, tutti i numeri binari possono essere rappresentati esattamente 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.125] [0.2825/3.125]

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

Quindi ti ritroverai con un numero superiore o inferiore al numero decimale specificato.Sempre.

Perché è importante?Arrotondamento.
L'arrotondamento normale significa 0..4 per difetto, 5..9 per eccesso.Così è fa importa se il risultato è neanche 0.049999999999....O 0.0500000000...Potresti sapere che significa 5 centesimi, ma il computer non lo sa e arrotonda 0.4999...giù (sbagliato) e 0.5000...in alto (a destra).
Dato che il risultato dei calcoli in virgola mobile contiene sempre piccoli termini di errore, la decisione è pura fortuna.Diventa inutile se si desidera una gestione decimale uniforme con numeri binari.

Poco convinta ?Insisti sul fatto che nel tuo sistema di account è tutto perfettamente a posto?
Attività e passività uguali?Ok, allora prendi ciascuno dei numeri formattati indicati per ciascuna voce, analizzali e sommali con un sistema decimale indipendente!Confrontalo con la somma formattata.
Ops, c'è qualcosa che non va, vero?

Per quel calcolo, erano necessarie un'estrema precisione e fedeltà (abbiamo usato Oracle's Float) in modo da poter registrare il "miliardo di un centesimo".

Non aiuta contro questo errore.Poiché tutte le persone presumono automaticamente che i conti del computer siano giusti, praticamente nessuno controlla in modo indipendente.

Altri suggerimenti

Per prima cosa dovresti leggere questo Ciò che ogni informatico dovrebbe sapere sull'aritmetica in virgola mobile.Allora dovresti davvero considerare l'utilizzo di qualche tipo di punto fisso/numero di precisione arbitraria pacchetto (es.java BigNum, modulo decimale python) altrimenti ti ritroverai in un mondo di dolore.Quindi scopri se è sufficiente utilizzare il tipo decimale SQL nativo.

Esistono (ndr) float/double per esporre il veloce x87 fp che ora è praticamente obsoleto.Non usarli se ti interessa l'accuratezza dei calcoli e/o non compensi completamente i loro limiti.

Come ulteriore avvertimento, SQL Server e .Net Framework utilizzano un algoritmo predefinito diverso per l'arrotondamento.Assicurati di controllare il parametro MidPointRounding in Math.Round()..Net Framework utilizza l'algoritmo Bankers per impostazione predefinita e SQL Server utilizza l'arrotondamento algoritmico simmetrico.Consulta l'articolo di Wikipedia Qui

Chiedi ai tuoi commercialisti!Ti disapproveranno per aver usato il float.Come qualcuno ha pubblicato prima, usa float SOLO se non ti interessa la precisione.Anche se sarei sempre contrario quando si tratta di soldi.

Nei software di contabilità NON è accettabile il flottante.Utilizza decimale con 4 cifre decimali.

I punti mobili hanno numeri irrazionali inaspettati.

Ad esempio, non puoi memorizzare 1/3 come decimale, sarebbe 0,3333333333...(e così via)

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

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

Utilizzando questi esponenti in base 2 si creano alcuni numeri irrazionali dispari, ad esempio:

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

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

Maggiori informazioni su questo problema su il mio blog, ma fondamentalmente, per l'archiviazione, è meglio usare i decimali.

Sul server Microsoft SQL hai il file money tipo di dati: in genere è la soluzione migliore per l'archiviazione finanziaria.È preciso fino a 4 posizioni decimali.

Per i calcoli hai più di un problema: l'imprecisione è una piccola frazione, ma mettila in una funzione di potenza e diventa rapidamente significativa.

Tuttavia i decimali non sono molto adatti per nessun tipo di matematica: ad esempio, non esiste un supporto nativo per le potenze decimali.

Utilizza il server SQL decimale tipo.

Non usare soldi O galleggiante.

il denaro utilizza 4 cifre decimali, è più veloce dell'utilizzo dei decimali MA soffre di alcuni problemi ovvi e altri meno ovvi con l'arrotondamento (vedere questo problema di connessione)

Quello che consiglierei è usare numeri interi a 64 bit che memorizzano il tutto in centesimi.

Un po' di background qui....

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

I numeri finanziari si basano su secoli di pratica di carta e penna, con convenzioni associate.Sono ragionevolmente accurati ma, cosa ancora più importante, sono riproducibili.Due contabili che lavorano con numeri e tariffe diverse dovrebbero fornire lo stesso numero.Qualsiasi spazio per discrepanze è spazio per frodi.

Pertanto, per i calcoli finanziari, la risposta giusta è qualunque cosa dia la stessa risposta di un commercialista bravo in aritmetica.Questa è aritmetica decimale, non in virgola mobile IEEE.

L'unico motivo per utilizzare Float per soldi è se non ti interessano 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é per la valuta sono consigliati i tipi decimali, anche se il problema della precisione potrebbe essere sufficientemente raro.

Per chiarire, il tipo decimal 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 della parte "maturazione degli interessi" del sistema.Ogni giorno, il mio codice calcolava quanti interessi erano stati maturati (guadagnati) sul saldo quel giorno.

Per tale calcolo erano necessarie estrema precisione e fedeltà (abbiamo utilizzato FLOAT di Oracle) in modo da poter registrare il "miliardesimo di centesimo" maturato.

Quando si trattava di "capitalizzare" gli interessi (es.rimborsando gli interessi sul tuo conto) l'importo è stato arrotondato al centesimo.Il tipo di dati per i saldi dei conti era di due cifre decimali.(In effetti era più complicato in quanto era un sistema multivaluta che poteva funzionare con molti decimali - ma arrotondavamo sempre al "penny" di quella valuta).Sì, lì esistevano "frazioni" di perdita e guadagno, ma quando le cifre dei computer venivano attualizzate (denaro pagato o versato) si trattava sempre di valori monetari REALI.

Ciò ha soddisfatto i contabili, i revisori dei conti e i tester.

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

Ancora meglio che usare i decimali è usare semplicemente i vecchi numeri interi (o forse qualche tipo di bigint).In questo modo si ha sempre la massima precisione possibile, 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 desideri avere maggiore precisione, puoi modificare 100 con un valore maggiore, come:10 ^ n, dove n è il numero di decimali.

Un'altra cosa di cui dovresti essere consapevole nei sistemi contabili è che nessuno dovrebbe avere accesso diretto alle tabelle.Ciò significa che tutti gli accessi al sistema contabile devono avvenire tramite procedure memorizzate.Questo previene le frodi e non solo gli attacchi SQL injection.Un utente interno che desidera commettere una frode non dovrebbe mai avere la possibilità di modificare direttamente i dati nelle tabelle del database.Questo è un controllo interno fondamentale sul tuo sistema.Vuoi davvero che qualche dipendente scontento vada nel backend del tuo database e inizi a scrivere loro assegni?O nascondere di aver approvato una spesa per un fornitore non autorizzato quando non dispone dell'autorità di approvazione?Solo due persone in tutta la tua organizzazione dovrebbero essere in grado di accedere direttamente ai dati nel tuo database finanziario, nel tuo dba e nel suo backup.Se disponi di molti database, solo due di essi dovrebbero avere questo accesso.

Ne parlo perché se i tuoi programmatori utilizzassero il float in un sistema contabile, probabilmente non avrebbero alcuna familiarità con l'idea dei controlli interni e non li avrebbero considerati nel loro sforzo di programmazione.

Puoi sempre scrivere qualcosa come un tipo Money per .Net.

Dai un'occhiata a questo articolo: Un tipo Money per CLR - Secondo me l'autore ha fatto un ottimo lavoro.

Stavo usando il tipo money di SQL per memorizzare valori monetari.Recentemente ho dovuto lavorare con diversi sistemi di pagamento online e ho notato che alcuni di essi utilizzano 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.

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

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

Hai considerato l'utilizzo del tipo di dati money per archiviare importi in dollari?

Per quanto riguarda il Con che il decimale occupa un byte in più, direi che non mi interessa.In 1 milione di righe utilizzerai solo 1 MB in più e lo spazio di archiviazione è molto economico al giorno d'oggi.

Qualunque cosa tu faccia, devi stare attento agli errori di arrotondamento.Calcola utilizzando un grado di precisione maggiore di quello visualizzato.

Probabilmente vorrai utilizzare una qualche forma di rappresentazione a punto fisso per i valori di valuta.Ti consigliamo inoltre di esaminare l'arrotondamento del banchiere (noto anche come "arrotondamento a metà pari"). Evita le distorsioni esistenti nel consueto metodo "arrotonda a metà per eccesso".

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

Hai tipi di dati valuta (money, smallmoney), che dovrebbero essere usati al posto di float o real.Memorizzare il decimale (12,2) eliminerà gli arrotondamenti, ma li eliminerà anche durante i passaggi intermedi, il che in realtà non è ciò che vorrai in un'applicazione finanziaria.

Utilizza sempre il decimale.Float ti fornirà valori imprecisi a causa di problemi di arrotondamento.

I numeri in virgola mobile possono soltanto rappresentano numeri che sono la somma di multipli negativi della base: per il sistema binario in virgola mobile, ovviamente, sono due.

Ci sono solo quattro frazioni decimali rappresentabili precisamente in virgola mobile binaria:0, 0,25, 0,5 e 0,75.Tutto il resto è un'approssimazione, allo stesso modo in cui 0,3333...è un'approssimazione per 1/3 nell'aritmetica decimale.

La virgola mobile è una buona scelta per i calcoli in cui la scala del risultato è ciò che è importante.È una cattiva scelta quando stai cercando di essere accurato fino a un certo numero di cifre decimali.

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

In sintesi, i valori esatti come il denaro dovrebbero utilizzare il decimale e i valori approssimativi come le misurazioni scientifiche dovrebbero utilizzare il float.

Ecco un esempio interessante che mostra che sia float che decimal sono in grado di perdere precisione.Quando si aggiunge un numero che non è un intero e poi si sottrae lo stesso numero float, si perde la precisione mentre decimal no:

    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 non intero e si divide per lo stesso numero, i decimali perdono precisione mentre i numeri in virgola mobile 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top