Cosa si può fare per i linguaggi di programmazione per evitare insidie ​​in virgola mobile?


28

Il fraintendimento dell'aritmetica in virgola mobile e delle sue carenze è una delle principali cause di sorpresa e confusione nella programmazione (si consideri il numero di domande su Stack Overflow relative a "numeri che non si aggiungono correttamente"). Considerando che molti programmatori devono ancora comprenderne le implicazioni, ha il potenziale per introdurre molti bug sottili (specialmente nel software finanziario). Cosa possono fare i linguaggi di programmazione per evitare le sue insidie per coloro che non hanno familiarità con i concetti, pur offrendo la sua velocità quando la precisione non è critico per coloro che fanno comprendere i concetti?


26
L'unica cosa che un linguaggio di programmazione può fare per evitare le insidie ​​dell'elaborazione in virgola mobile è vietarlo. Si noti che ciò include anche la virgola mobile in base 10, che è altrettanto imprecisa in generale, tranne che le applicazioni finanziarie sono pre-adattate ad esso.
David Thornley,

4
Ecco a cosa serve "Analisi numerica". Scopri come ridurre al minimo la perdita di precisione, ovvero le insidie ​​in virgola mobile.

Un buon esempio di problema in virgola mobile: stackoverflow.com/questions/10303762/0-0-0-0-0
Austin Henley

Risposte:


47

Dici "soprattutto per il software finanziario", che fa apparire uno dei miei animali domestici: il denaro non è un galleggiante, è un int .

Certo, sembra un galleggiante. Ha un punto decimale lì dentro. Ma è solo perché sei abituato alle unità che confondono il problema. Il denaro arriva sempre in quantità intere. In America sono i centesimi. (In alcuni contesti penso che possano essere mulini , ma per ora ignoralo.)

Quindi quando dici $ 1,23, sono davvero 123 centesimi. Sempre, sempre, fai sempre la tua matematica in questi termini e starai bene. Per ulteriori informazioni, vedere:

Rispondendo direttamente alla domanda, i linguaggi di programmazione dovrebbero includere un tipo di denaro come ragionevole primitivo.

aggiornare

Ok, avrei dovuto dire "sempre" solo due volte, anziché tre volte. Il denaro è davvero sempre un int; coloro che la pensano diversamente sono invitati a provare a inviarmi 0,3 centesimi e mostrarmi il risultato sul tuo estratto conto. Ma come sottolineano i commentatori, ci sono rare eccezioni quando è necessario fare matematica in virgola mobile su numeri simili a soldi. Ad esempio, alcuni tipi di prezzi o calcoli di interessi. Anche allora, quelli dovrebbero essere trattati come eccezioni. Il denaro entra ed esce come quantità intere, quindi più il sistema si avvicina a ciò, più sarà sanato.


20
@JoelFan: stai confondendo un concetto per un'implementazione specifica della piattaforma.
whatsisname

12
Non è così semplice. I calcoli degli interessi, tra gli altri, producono centesimi frazionari e devono essere arrotondati ad un certo punto secondo un metodo specificato.
Kevin Cline,

24
Fiction -1, dal momento che non ho la reputazione di un voto negativo :) ... Questo potrebbe essere corretto per tutto ciò che è nel tuo portafoglio ma ci sono molte situazioni contabili in cui potresti avere a che fare con decimi di centesimo o frazioni più piccole. Decimalè l'unico sistema sano di fronte a questo, e il tuo commento "ignoralo per ora" è il presagio del destino per i programmatori di tutto il mondo: P
detly

9
@kevin cline: ci sono centesimi frazionari nei calcoli, ma ci sono convenzioni su come gestirli. L'obiettivo dei calcoli finanziari non è la correttezza matematica, ma ottenere gli stessi esatti risultati che farebbe un banchiere con una calcolatrice.
David Thornley,

6
Tutto sarà perfetto sostituendo la parola "intero" con "razionale" -
Emilio Garavaglia,

15

Fornire supporto per un tipo decimale aiuta in molti casi. Molte lingue hanno un tipo decimale, ma sono sottoutilizzate.

È importante comprendere l'approssimazione che si verifica quando si lavora con la rappresentazione di numeri reali. L'uso di entrambi i tipi decimali e in virgola mobile 9 * (1/9) != 1è un'istruzione corretta. Quando costanti un ottimizzatore può ottimizzare il calcolo in modo che sia corretto.

Fornire un operatore approssimativo sarebbe di aiuto. Tuttavia, tali confronti sono problematici. Si noti che 0,999 trilioni di dollari equivalgono approssimativamente a 1 trilione di dollari. Potresti depositare la differenza sul mio conto bancario, per favore?


2
0.9999...trilioni di dollari sono esattamente pari a 1 trilione di dollari in realtà.
SOLO IL MIO OPINIONE corretta il

5
@JUST: Sì, ma non ho riscontrato alcun computer con i registri che possono contenere 0.99999.... Troncano tutti ad un certo punto con conseguente disuguaglianza. 0.9999è abbastanza uguale per l'ingegneria. A fini finanziari non lo è.
BillThor,

2
Ma quale tipo di sistema utilizzava trilioni di dollari come unità base anziché quelli di dollari?
Brad

@Brad Prova a calcolare (1 Trilione / 3) * 3 sulla tua calcolatrice. Che valore ottieni?
BillThor,

8

Ci è stato detto cosa fare durante il primo anno (secondo anno) di lezione di informatica quando sono andato all'università, (questo corso era un prerequisito per la maggior parte dei corsi di scienze)

Ricordo il docente che diceva "I numeri in virgola mobile sono approssimazioni. Usa tipi interi per denaro. Usa FORTRAN o altra lingua con numeri BCD per un calcolo accurato." (e poi ha sottolineato l'approssimazione, usando quel classico esempio di 0,2 impossibile da rappresentare accuratamente in virgola mobile binaria). Ciò è emerso anche quella settimana negli esercizi di laboratorio.

Stessa lezione: "Se devi ottenere una maggiore precisione da virgola mobile, ordina i termini. Aggiungi numeri piccoli insieme, non a numeri grandi." Mi è rimasto bloccato nella mente.

Qualche anno fa avevo una geometria sferica che doveva essere molto accurata e comunque veloce. Il doppio di 80 bit sui PC non lo stava tagliando, quindi ho aggiunto alcuni tipi al programma che ordinavano i termini prima di eseguire operazioni commutative. Problema risolto.

Prima di lamentarti della qualità della chitarra, impara a suonare.

Ho avuto un collega quattro anni fa che aveva lavorato per JPL. Ha espresso incredulità sul fatto che abbiamo usato FORTRAN per alcune cose. (Avevamo bisogno di simulazioni numeriche super accurate calcolate offline.) "Abbiamo sostituito tutto quel FORTRAN con C ++", ha detto con orgoglio. Ho smesso di chiedermi perché hanno perso un pianeta.


2
+1 lo strumento giusto per il lavoro giusto. Anche se in realtà non uso FORTRAN. Per fortuna, né lavoro sui nostri sistemi finanziari al lavoro.
James Khoury,

"Se devi ottenere maggiore precisione da virgola mobile, ordina i termini. Aggiungi piccoli numeri insieme, non a grandi numeri." Qualche esempio su questo?
mamcx,

@mamcx Immagina un numero decimale in virgola mobile con una sola cifra di precissione. Il calcolo 1.0 + 0.1 + ... + 0.1(ripetuto 10 volte) ritorna 1.0quando ogni risultato intermedio viene arrotondato. Farlo il contrario, si ottengono risultati intermedi di 0.2, 0.3, ..., 1.0e infine 2.0. Questo è un esempio estremo, ma con numeri in virgola mobile realistici, si verificano problemi simili. L'idea di base è che l'aggiunta di numeri di dimensioni simili porta all'errore più piccolo. Inizia con i numeri più piccoli in quanto la loro somma è maggiore e quindi più adatta per l'aggiunta a quelli più grandi.
Maaartinus,

Le cose in virgola mobile in Fortran e C ++ saranno per lo più identiche. Entrambi sono precisi e offline, e sono abbastanza sicuro che Fortran non abbia real BCD nativi ...
Mark

8

Avvertenza: il tipo a virgola mobile System.Double non ha la precisione per i test diretti sull'uguaglianza.

double x = CalculateX();
if (x == 0.1)
{
    // ............
}

Non credo che qualcosa possa o debba essere fatto a livello linguistico.


1
Non uso un float o un double da molto tempo, quindi sono curioso. È un avviso del compilatore esistente o solo quello che vorresti vedere?
Karl Bielefeldt,

1
@Karl - Personalmente non l'ho visto o ne ho bisogno, ma immagino che possa essere utile per sviluppatori dedicati ma ecologici.
ChaosPandion,

1
I tipi binari a virgola mobile non sono migliori o peggiori qualitativamente rispetto a Decimalquando si tratta di test di uguaglianza. La differenza tra 1.0m/7.0m*7.0me 1.0mpotrebbe essere molti ordini di grandezza inferiore alla differenza tra 1.0/7.0*7.0, ma non è zero.
supercat

1
@Patrick - Non sono sicuro di cosa stai arrivando. C'è un'enorme differenza tra qualcosa di vero per un caso e di essere vero per tutti i casi.
ChaosPandion

1
@ChaosPandion Il problema con l'esempio in questo post non è il confronto di uguaglianza, è letterale in virgola mobile. Non c'è float con il valore esatto 1.0 / 10. La matematica in virgola mobile fornisce risultati accurati al 100% quando si calcola con numeri interi che si adattano alla mantissa.
Patrick

7

Per impostazione predefinita, le lingue dovrebbero usare razionali di precisione arbitraria per numeri non interi.

Chi ha bisogno di ottimizzare può sempre chiedere galleggianti. Usarli come predefiniti aveva senso in C e in altri linguaggi di programmazione dei sistemi, ma non nella maggior parte dei linguaggi popolari oggi.


1
Come gestisci i numeri irrazionali allora?
dsimcha,

3
Lo fai allo stesso modo dei float: approssimazione.
Waquo,

1
Devo dire che penso che questo abbia molto senso, la maggior parte delle persone che hanno bisogno di numeri esatti hanno bisogno di razionali e non di irrazionali (la scienza e l'ingegneria possono usare le irrazionali ma poi sei di nuovo nel regno approssimativo, o stai facendo qualche matematica pura piuttosto specializzata)
jk.

1
I calcoli con razionali di precisione arbitraria saranno spesso ordini di grandezza più lenti (forse MOLTI ordini di grandezza più lenti) rispetto ai calcoli con un supporto hardware double. Se un calcolo deve essere accurato per una parte per milione, è meglio impiegare un microsecondo per computarlo entro poche parti per miliardo, piuttosto che dedicare un secondo per elaborarlo in modo assolutamente preciso.
supercat

5
@supercat: Quello che stai suggerendo è solo un figlio dell'ottimizzazione prematura. La situazione attuale è che la stragrande maggioranza dei programmatori non ha bisogno di alcun tipo per la matematica veloce e quindi viene morso da un comportamento a virgola mobile (mis) difficile da capire, in modo che il numero relativamente piccolo di programmatori che hanno bisogno di matematica veloce ottenga senza per digitare un singolo carattere aggiuntivo. Questo aveva senso negli anni Settanta, ora è solo una sciocchezza. L'impostazione predefinita dovrebbe essere sicura. Chi ha bisogno di digiunare dovrebbe chiederlo.
Waquo

4

I due maggiori problemi che coinvolgono numeri in virgola mobile sono:

  • unità incoerenti applicate ai calcoli (si noti che ciò influisce anche sull'aritmetica dei numeri interi allo stesso modo)
  • mancata comprensione del fatto che i numeri FP sono un'approssimazione e come gestire in modo intelligente l'arrotondamento.

Il primo tipo di errore può essere risolto solo fornendo un tipo composito che include informazioni sul valore e sull'unità. Ad esempio, un valore lengtho areache incorpora l'unità (metri o metri quadrati o piedi e piedi quadrati rispettivamente). Altrimenti devi essere diligente nel lavorare sempre con un tipo di unità di misura e convertirti in un altro solo quando condividiamo la risposta con un essere umano.

Il secondo tipo di fallimento è un fallimento concettuale. I fallimenti si manifestano quando le persone li considerano come numeri assoluti . Influisce su operazioni di uguaglianza, errori di arrotondamento cumulativo, ecc. Ad esempio, può essere corretto che per un sistema due misurazioni siano equivalenti entro un certo margine di errore. Vale a dire .999 e 1.001 sono all'incirca uguali a 1.0 quando non ti interessano le differenze inferiori a +/- .1. Tuttavia, non tutti i sistemi sono così indulgenti.

Se è necessaria una struttura a livello linguistico, la definirei precisione di uguaglianza . In NUnit, JUnit e framework di test costruiti in modo simile è possibile controllare la precisione considerata corretta. Per esempio:

Assert.That(.999, Is.EqualTo(1.001).Within(10).Percent);
// -- or --
Assert.That(.999, Is.EqualTo(1.001).Within(.1));

Se, ad esempio, C # o Java fossero stati modificati per includere un operatore di precisione, potrebbe assomigliare a questo:

if(.999 == 1.001 within .1) { /* do something */ }

Tuttavia, se si fornisce una funzionalità del genere, è necessario considerare anche il caso in cui l'uguaglianza è buona se i lati +/- non sono gli stessi. Ad esempio, + 1 / -10 considererebbe due numeri equivalenti se uno di essi fosse compreso tra 1 in più o 10 in meno del primo numero. Per gestire questo caso, potrebbe essere necessario aggiungere anche una rangeparola chiave:

if(.999 == 1.001 within range(.001, -.1)) { /* do something */ }

2
Commuterei l'ordine. Il problema concettuale è pervasivo. Il problema di conversione delle unità è relativamente minore in confronto.
S.Lott

Mi piace il concetto di operatore di precisione ma, come accennerai più avanti, avrebbe sicuramente bisogno di essere ben ponderato. Personalmente sarei più propenso a vederlo come il suo completo costrutto sintattico.
ChaosPandion,

Potrebbe anche essere fatto molto facilmente in una biblioteca.
Michael K,

1
@ dan04: stavo pensando di più in termini di "tutti i calcoli accurati entro l'uno per cento" o simili. Ho visto il tar-pit che è l'unità di misura dell'unità e sto bene.
TMN,

1
Circa 25 anni fa, ho visto un pacchetto numerico con un tipo costituito da una coppia di numeri in virgola mobile che rappresentano i valori massimi e minimi possibili per una quantità. Man mano che i numeri passavano attraverso i calcoli, la differenza tra massimo e minimo aumenterebbe. In effetti, ciò ha fornito un mezzo per sapere quanta precisione reale era presente in un valore calcolato.
supercat

3

Cosa possono fare i linguaggi di programmazione? Non so se c'è una risposta a questa domanda, perché tutto ciò che il compilatore / interprete fa per conto del programmatore per rendere più semplice la sua vita di solito funziona contro prestazioni, chiarezza e leggibilità. Penso che sia il modo C ++ (paga solo per quello che ti serve) sia il modo Perl (principio della minima sorpresa) sono entrambi validi, ma dipende dall'applicazione.

I programmatori devono ancora lavorare con la lingua e capire come gestisce i punti mobili, perché in caso contrario, faranno ipotesi e un giorno il comportamento trascritto non corrisponderà alle loro ipotesi.

La mia opinione su ciò che il programmatore deve sapere:

  • Quali tipi in virgola mobile sono disponibili sul sistema e nella lingua
  • Che tipo è necessario
  • Come esprimere le intenzioni di quale tipo è necessario nel codice
  • Come sfruttare correttamente qualsiasi promozione automatica del tipo per bilanciare chiarezza ed efficienza mantenendo la correttezza

3

Cosa possono fare i linguaggi di programmazione per evitare insidie ​​[in virgola mobile] ...?

Utilizzare impostazioni predefinite sensibili, ad esempio il supporto integrato per i decmial.

Groovy lo fa abbastanza bene, anche se con un po 'di sforzo puoi ancora scrivere codice per introdurre l'imprecisione in virgola mobile.


3

Sono d'accordo che non c'è niente da fare a livello linguistico. I programmatori devono capire che i computer sono discreti e limitati e che molti dei concetti matematici rappresentati in essi sono solo approssimazioni.

Non importa in virgola mobile. Bisogna capire che metà dei modelli di bit sono usati per numeri negativi e che 2 ^ 64 è in realtà piuttosto piccolo per evitare problemi tipici con l'aritmetica dei numeri interi.


non sono d'accordo, la maggior parte delle lingue attualmente fornisce troppo supporto per i tipi binari in virgola mobile (perché == è persino definita per i float?) e non abbastanza supporto per razionali o decimali
jk.

@jk: anche se il risultato di qualsiasi calcolo non sarebbe mai garantito uguale al risultato di nessun altro calcolo, il confronto di uguaglianza sarebbe comunque utile nel caso in cui lo stesso valore venga assegnato a due variabili (anche se le regole di uguaglianza comunemente implementate sono forse troppo lento, poiché x== ynon implica che eseguire un calcolo su xprodurrà lo stesso risultato di eseguire lo stesso calcolo su y).
supercat

@supercat hai ancora bisogno di un confronto, ma preferirei che la lingua mi richiedesse di specificare una tolleranza per ogni confronto in virgola mobile, quindi posso ancora tornare all'uguaglianza scegliendo tolleranza = 0, ma sono almeno costretto a farlo scelta
jk.

3

Una cosa che le lingue potrebbero fare: rimuovere il confronto di uguaglianza da tipi in virgola mobile diversi da un confronto diretto con i valori NAN.

Il test di uguaglianza esisterebbe solo come chiamata di funzione che ha preso i due valori e un delta, o per linguaggi come C # che consentono ai tipi di avere metodi un EqualsTo che accetta l'altro valore e il delta.


3

Trovo strano che nessuno abbia sottolineato il trucco razionale della famiglia Lisp.

Seriamente, apri sbcl e fai questo: (+ 1 3)e ottieni 4. Se *( 3 2)ottieni 6. Ora prova (/ 5 3)e ottieni 5/3 o 5 terzi.

Ciò dovrebbe aiutare un po 'in alcune situazioni, non è vero?


Mi chiedo, se possibile, sapere se un risultato deve essere rappresentato come 1/3 o potrebbe essere un decimale esatto?
mamcx,

buon suggerimento
Peter Porfy,

3

Una cosa che mi piacerebbe vedere sarebbe un riconoscimento che doubleper floatdeve essere considerata come una conversione ampliamento, mentre floata doublesi sta riducendo (*). Ciò può sembrare controintuitivo, ma considera cosa significano effettivamente i tipi:

  • 0.1f significa "13.421.773,5 / 134.217.728, più o meno 1 / 268.435.456 o così".
  • 0.1 significa veramente 3.602.879.701.896.397 / 36.028.797.018.963.968, più o meno 1 / 72.057.594.037.927.936 o così "

Se uno ha un valore doubleche rappresenta la migliore rappresentazione della quantità "un decimo" e lo converte in float, il risultato sarà "13.421.773,5 / 134.217.728, più o meno 1 / 268.435.456 o giù di lì", che è una descrizione corretta del valore.

Al contrario, se uno ha un valore floatche rappresenta la migliore rappresentazione della quantità "un decimo" e lo converte in double, il risultato sarà "13.421.773,5 / 134.217.728, più o meno 1 / 72.057.594.037.927.936 circa" - un livello di precisione implicita che è sbagliato da un fattore di oltre 53 milioni.

Sebbene lo standard IEEE-744 richieda che la matematica in virgola mobile venga eseguita come se ogni numero in virgola mobile rappresenti esattamente la quantità numerica esattamente al centro del suo intervallo, ciò non dovrebbe implicare che i valori in virgola mobile rappresentino effettivamente quelli esatti quantità numeriche. Piuttosto, il requisito secondo cui i valori devono essere considerati al centro dei loro intervalli deriva da tre fatti: (1) i calcoli devono essere eseguiti come se gli operandi abbiano dei valori precisi; (2) ipotesi coerenti e documentate sono più utili di quelle incoerenti o non documentate; (3) se si intende fare un'ipotesi coerente, nessun'altra ipotesi coerente può essere migliore dell'ipotesi che una quantità rappresenti il ​​centro del suo intervallo.

Per inciso, ricordo circa 25 anni fa, qualcuno inventò un pacchetto numerico per C che utilizzava "tipi di intervallo", ciascuno costituito da una coppia di float a 128 bit; tutti i calcoli verrebbero eseguiti in modo tale da calcolare il valore minimo e massimo possibile per ciascun risultato. Se si eseguisse un grande calcolo iterativo lungo e si ottenesse un valore di [12.53401391134 12.53902812673], si potrebbe essere certi che mentre molte cifre di precisione andavano perse a causa di errori di arrotondamento, il risultato poteva ancora essere ragionevolmente espresso come 12,54 (e non lo era ' veramente 12.9 o 53.2). Sono sorpreso di non aver visto alcun supporto per tali tipi in nessun linguaggio tradizionale, soprattutto perché sembrerebbero adatti a unità matematiche che possono operare su più valori in parallelo.

(*) In pratica, è spesso utile utilizzare valori di precisione doppia per contenere calcoli intermedi quando si lavora con numeri a precisione singola, quindi dover usare un typecast per tutte queste operazioni potrebbe essere fastidioso. Le lingue potrebbero aiutare avendo un tipo "fuzzy double", che eseguirà i calcoli come double, e potrebbe essere liberamente trasmesso da e verso single; ciò sarebbe particolarmente utile se le funzioni che accettano parametri di tipo doublee di ritorno doublepotrebbero essere contrassegnate in modo da generare automaticamente un sovraccarico che accetta e restituisce invece "fuzzy double".


2

Se più linguaggi di programmazione prendessero una pagina dai database e consentissero agli sviluppatori di specificare la lunghezza e la precisione dei loro tipi di dati numerici, potrebbero ridurre sostanzialmente la probabilità di errori in virgola mobile. Se una lingua permettesse a uno sviluppatore di dichiarare una variabile come Float (2), indicando che avevano bisogno di un numero in virgola mobile con due cifre decimali di precisione, potrebbe eseguire operazioni matematiche molto più in sicurezza. Se lo facesse rappresentando la variabile come numero intero internamente e dividendolo per 100 prima di esporre il valore, potrebbe migliorare la velocità utilizzando i percorsi aritmetici interi più veloci. La semantica di un Float (2) consentirebbe inoltre agli sviluppatori di evitare la costante necessità di arrotondare i dati prima di inviarli poiché un Float (2) arrotonda intrinsecamente i dati a due punti decimali.

Ovviamente, dovresti consentire a uno sviluppatore di chiedere un valore in virgola mobile di massima precisione quando lo sviluppatore deve avere quella precisione. E introduresti problemi in cui espressioni leggermente diverse della stessa operazione matematica producono risultati potenzialmente diversi a causa di operazioni di arrotondamento intermedio quando gli sviluppatori non portano abbastanza precisione nelle loro variabili. Ma almeno nel mondo dei database, questo non sembra essere un grosso problema. La maggior parte delle persone non esegue i calcoli scientifici che richiedono molta precisione nei risultati intermedi.


Specificare lunghezza e precisione farebbe ben poco che sia utile. Avere una base fissa 10 sarebbe utile per l'elaborazione finanziaria, il che eliminerebbe gran parte della sorpresa che le persone ottengono dal punto mobile.
David Thornley,

@ David - Forse mi manca qualcosa, ma in che modo un tipo di dati a base fissa 10 è diverso da quello che sto proponendo qui? Un Float (2) nel mio esempio dovrebbe avere 2 cifre decimali fisse e arrotondare automaticamente al centesimo più vicino, che è quello che probabilmente useresti per semplici calcoli finanziari. Calcoli più complessi richiederebbero allo sviluppatore di assegnare un numero maggiore di cifre decimali.
Grotta di Giustino,

1
Quello che stai sostenendo è un tipo di dati di base 10 a virgola fissa con precisione specificata dal programmatore. Sto dicendo che la precisione specificata dal programmatore è in gran parte inutile e porterà solo al tipo di errori che ho incontrato nei programmi COBOL. (Ad esempio, quando si modifica la precisione delle variabili, è davvero facile perdere una variabile che attraversa il valore. Per un'altra, ci vorrà molto di più pensando alle dimensioni intermedie dei risultati che a una buona.)
David Thornley

4
Un Float(2)come tu proponi non dovrebbe essere chiamato Float, dal momento che qui non c'è nulla che galleggia, certamente non il "punto decimale".
Paŭlo Ebermann,

1
  • le lingue hanno il supporto del tipo decimale; ovviamente questo non risolve davvero il problema, ma non hai ancora una rappresentazione esatta e limitata di ⅓;
  • alcuni DB e framework hanno il supporto del tipo di denaro, questo in pratica memorizza il numero di centesimi come numero intero;
  • ci sono alcune librerie per il supporto di numeri razionali; che risolve il problema di ⅓, ma non risolve il problema ad esempio √2;

Queste sopra sono applicabili in alcuni casi, ma in realtà non rappresentano una soluzione generale per gestire i valori float. La vera soluzione è capire il problema e imparare come affrontarlo. Se stai usando calcoli in virgola mobile, dovresti sempre verificare se i tuoi algoritmi sono numericamente stabili . C'è un enorme campo di matematica / informatica che si riferisce al problema. Si chiama Analisi numerica .


1

Come hanno notato altre risposte, l'unico vero modo per evitare insidie ​​in virgola mobile nel software finanziario è di non utilizzarlo lì. Questo può effettivamente essere fattibile se fornisci una biblioteca ben progettata dedicata alla matematica finanziaria .

Le funzioni progettate per importare stime in virgola mobile devono essere chiaramente etichettate come tali e dotate di parametri adeguati a tale operazione, ad esempio:

Finance.importEstimate(float value, Finance roundingStep)

L'unico vero modo per evitare le insidie ​​in virgola mobile in generale è l'educazione: i programmatori devono leggere e comprendere qualcosa come ciò che ogni programmatore dovrebbe sapere sull'aritmetica in virgola mobile .

Alcune cose che potrebbero aiutare, però:

  • Riconoscerò coloro che chiedono "perché i test di uguaglianza esatta per il virgola mobile sono persino legali?"
  • Invece, usa una isNear()funzione.
  • Fornire e incoraggiare l'uso di oggetti accumulatori a virgola mobile (che aggiungono sequenze di valori in virgola mobile in modo più stabile rispetto alla semplice aggiunta di tutti in una normale variabile in virgola mobile).

-1

La maggior parte dei programmatori sarebbe sorpresa del fatto che COBOL abbia capito bene ... nella prima versione di COBOL non c'erano punti in virgola mobile, solo decimali, e la tradizione in COBOL è continuata fino ad oggi che la prima cosa che pensi quando dichiari un numero è decimale. .. il virgola mobile verrebbe utilizzato solo se davvero necessario. Quando è arrivato C, per qualche motivo, non c'era un tipo decimale primitivo, quindi secondo me è lì che sono iniziati tutti i problemi.


1
C non aveva un tipo decimale perché non è primitivo, pochissimi computer hanno una sorta di istruzioni decimali hardware. Potresti chiederti perché BASIC e Pascal non ce l'hanno, dal momento che non sono stati progettati per conformarsi strettamente al metallo. COBOL e PL / I sono le uniche lingue che conosco del tempo che avevano qualcosa del genere.
David Thornley,

3
@JoelFan: quindi come scrivi ⅓ in COBOL? Il decimale non risolve alcun problema, la base 10 è imprecisa quanto la base 2.
vartec,

2
Decimal risolve il problema di rappresentare esattamente dollari e centesimi, utile per un linguaggio "orientato al business". Altrimenti, il decimale è inutile; ha gli stessi tipi di errori (ad es. 1/3 * 3 = 0.99999999) pur essendo molto più lento. Questo è il motivo per cui non è l'impostazione predefinita nelle lingue che non sono state progettate specificamente per la contabilità.
dan04

1
E FORTRAN, che precede C di oltre un decennio, non ha nemmeno il supporto decimale standard.
dan04

1
@JoelFan: se hai un valore trimestrale e hai bisogno di un valore mensile, indovina cosa devi moltiplicarlo per ... no, non è 0,33, è ⅓.
vartec,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.