Domanda

Scrivo applicazioni finanziarie in cui combatto costantemente la decisione di usare un doppio contro un decimale.

Tutta la mia matematica funziona su numeri con non più di 5 cifre decimali e non sono più grandi di ~ 100.000. Ho la sensazione che tutti questi possano essere rappresentati come doppi comunque senza errori di arrotondamento, ma non sono mai stato sicuro.

Vorrei andare avanti e passare dai decimali ai doppi per l'ovvio vantaggio di velocità, tranne che alla fine della giornata, uso ancora il metodo ToString per trasmettere i prezzi agli scambi e devo assicurarmi che sia sempre in uscita il numero che mi aspetto. (89.99 anziché 89.99000000001)

Domande:

  1. Il vantaggio di velocità è davvero grande come suggeriscono i test ingenui? (~ 100 volte)
  2. C'è un modo per garantire che l'output di ToString sia quello che voglio? Questo è garantito dal fatto che il mio numero è sempre rappresentabile?

AGGIORNAMENTO: devo elaborare circa 10 miliardi di aggiornamenti di prezzo prima che la mia app possa essere eseguita e l'ho implementata con decimali proprio ora per ovvi motivi protettivi, ma ci vogliono ~ 3 ore solo per accendersi, i doppi ridurrebbero drasticamente il mio accendere il tempo. C'è un modo sicuro per farlo con i doppi?

È stato utile?

Soluzione

  1. L'aritmetica in virgola mobile sarà quasi sempre significativamente più veloce perché è supportata direttamente dall'hardware. Finora quasi nessun hardware ampiamente utilizzato supporta l'aritmetica decimale (anche se questo sta cambiando, vedi commenti).
  2. Le applicazioni finanziarie dovrebbero sempre usare numeri decimali, il numero di storie dell'orrore derivanti dall'uso di virgola mobile nelle applicazioni finanziarie è infinito, dovresti essere in grado di trovare molti di questi esempi con una ricerca su Google.
  3. Mentre l'aritmetica decimale può essere significativamente più lenta dell'aritmetica in virgola mobile, a meno che non si stia impiegando molto tempo a elaborare i dati decimali, è probabile che l'impatto sul programma sia trascurabile. Come sempre, esegui la profilazione appropriata prima di iniziare a preoccuparti della differenza.

Altri suggerimenti

Ci sono due problemi separabili qui. Uno è se il doppio ha abbastanza precisione per contenere tutti i bit necessari e l'altro è dove può rappresentare esattamente i tuoi numeri.

Per quanto riguarda la rappresentazione esatta, hai ragione a essere cauto, perché una frazione decimale esatta come 1/10 non ha una controparte binaria esatta. Tuttavia, se sai che hai bisogno solo di 5 cifre decimali di precisione, puoi usare l'aritmetica in scala in cui operi su numeri moltiplicati per 10 ^ 5. Ad esempio, se vuoi rappresentare esattamente 23.7205, lo rappresenti come 2372050.

Vediamo se c'è abbastanza precisione: la doppia precisione ti dà 53 bit di precisione. Ciò equivale a oltre 15 cifre decimali di precisione. Quindi questo ti permetterebbe cinque cifre dopo il punto decimale e 10 cifre prima del punto decimale, il che sembra ampio per la tua applicazione.

Vorrei inserire questo codice C in un file .h:

typedef double scaled_int;

#define SCALE_FACTOR 1.0e5  /* number of digits needed after decimal point */

static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; }
static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; }

static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; }
static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); }
static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); }

void fprint_scaled(FILE *out, scaled_int x) {
  fprintf(out, "%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x));
}

Probabilmente ci sono alcuni punti difficili ma questo dovrebbe essere sufficiente per iniziare.

Nessun sovraccarico per aggiunta, costo di una moltiplicazione o di una divisione dei doppi.

Se hai accesso a C99, puoi anche provare l'aritmetica di numeri interi in scala usando il tipo di numero intero a 64 bit int64_t . Ciò che è più veloce dipenderà dalla tua piattaforma hardware.

Usa sempre il decimale per qualsiasi calcolo finanziario o inseguirai per sempre errori di arrotondamento di 1 cent.

  1. Sì; l'aritmetica del software è 100 volte più lenta dell'hardware. O, almeno, è molto più lento, e un fattore di 100, dare o prendere un ordine di grandezza, è giusto. Ai vecchi tempi in cui non si poteva supporre che ogni 80386 avesse un coprocessore a virgola mobile 80387, quindi si aveva anche una simulazione software del virgola mobile binaria, e questo era lento.
  2. Nessun; stai vivendo in una terra di fantasia se pensi che un punto mobile binario puro possa mai rappresentare esattamente tutti i numeri decimali. I numeri binari possono combinare metà, quarti, ottavi, ecc., Ma poiché un decimale esatto di 0,01 richiede due fattori di un quinto e un fattore di un quarto (1/100 = (1/4) * (1/5) * (1 / 5)) e poiché un quinto non ha una rappresentazione esatta in binario, non è possibile rappresentare esattamente tutti i valori decimali con valori binari (poiché 0,01 è un controesempio che non può essere rappresentato esattamente, ma è rappresentativo di un'enorme classe di numeri decimali che non può essere rappresentato esattamente).

Quindi, devi decidere se puoi gestire l'arrotondamento prima di chiamare ToString () o se devi trovare qualche altro meccanismo che gestirà l'arrotondamento dei risultati quando vengono convertiti in una stringa. Oppure puoi continuare a usare l'aritmetica decimale poiché rimarrà accurata e diventerà più veloce una volta rilasciate le macchine che supportano la nuova aritmetica decimale IEEE 754 nell'hardware.

Riferimenti incrociati obbligatori: Che cosa dovrebbero sapere tutti gli informatici Informazioni sull'aritmetica in virgola mobile . Questo è uno dei tanti possibili URL.

Informazioni sull'aritmetica decimale e il nuovo standard IEEE 754: 2008 in questo sito Speleotrove .

Usa solo un long e moltiplica per un potere di 10. Dopo aver finito, dividi per lo stesso potere di 10.

I decimali devono sempre essere utilizzati per i calcoli finanziari. La dimensione dei numeri non è importante.

Il modo più semplice per spiegarlo è tramite un codice C #.

double one = 3.05;
double two = 0.05;

System.Console.WriteLine((one + two) == 3.1);

Quel codice verrà stampato Falso anche se 3.1 è uguale a 3.1 ...

Stessa cosa ... ma usando il decimale:

decimal one = 3.05m;
decimal two = 0.05m;

System.Console.WriteLine((one + two) == 3.1m);

Ora verrà stampato Vero !

Se vuoi evitare questo tipo di problema, ti consiglio di rispettare i decimali.

Ti rimando alla mia risposta data a questa domanda .

Utilizza un valore lungo, archivia la quantità minima necessaria per tracciare e visualizza i valori di conseguenza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top