Perché hai bisogno di float / double?


29

Stavo guardando http://www.joelonsoftware.com/items/2011/06/27.html e ho riso della battuta di Jon Skeet circa 0,3 non 0,3. Personalmente non ho mai avuto problemi con float / decimali / doppi ma poi ricordo di aver imparato molto presto 6502 e non ho mai avuto bisogno di float nella maggior parte dei miei programmi. L'unica volta che l'ho usato è stato per la grafica e la matematica in cui i numeri inesatti erano ok e l'output era per lo schermo e non essere archiviato (in un db, file) o dipendente da.

La mia domanda è: dove sono in genere i luoghi in cui utilizzavi float / decimali / double? Quindi so di stare attento a questi gotcha. Con i soldi uso i long e immagazzino i valori in centesimi, per la velocità di un oggetto in un gioco aggiungo ints e divido (o bit) il valore per sapere se devo spostare un pixel o no. (Ho fatto muovere gli oggetti nei 6502 giorni, non avevamo né divisioni né galleggianti ma avevamo turni).

Quindi ero per lo più curioso.


10
perché è molto importante che l'interesse che pago sul mio mutuo rimanga 12,6 e diventi 13 solo perché 13 è un numero tondo così bello.
Chani,

1
"Ho imparato molto presto 6502 e non ho mai avuto bisogno di float nella maggior parte dei miei programmi ... per la velocità di un oggetto aggiungo ints e divido il valore per sapere se spostare un pixel o no." Questi sono modi molto insoliti per svolgere questi compiti nella pratica moderna, ad eccezione della rappresentazione del denaro come centesimi lunghi.
giovedì

Meno male che il computer capisce i millicenti.
tylermac,

1
O inoltre, perché usare i decimali quando possiamo usare le frazioni?
tylermac,

6
@Scrooge - ironicamente non puoi rappresentare 0.6 in un float.
Martin Beckett,

Risposte:


28

Perché sono, per la maggior parte degli scopi, più precisi degli interi.

Ora come va? "per la velocità di un oggetto in un gioco ..." questo è un buon esempio in questo caso. Di 'che devi avere degli oggetti molto veloci, come i proiettili. Per poter descrivere il loro movimento con variabili di velocità intere, è necessario assicurarsi che le velocità siano nell'intervallo delle variabili intere, ciò significa che non è possibile avere un raster arbitrariamente fine.

Ma poi, potresti anche voler descrivere alcuni oggetti molto lenti, come la lancetta delle ore di un orologio. Poiché questo è di circa 6 ordini di grandezza più lento degli oggetti proiettile, i primi ld (10⁶) ≈ 20 bit sono zero, che esclude i short inttipi dall'inizio. Ok, oggi abbiamo longs ovunque, che ci lascia con 12 bit ancora comodi. Ma anche in questo caso, la velocità di clock sarà esatta solo con quattro cifre decimali. Non è un ottimo orologio ... ma sicuramente va bene per un gioco. Solo, non vorresti rendere il raster molto più grossolano di quanto non sia già.

... il che porta a problemi se un giorno ti piacerebbe introdurre un nuovo tipo di oggetto ancora più veloce. Non c'è più "spazio per la testa".

Cosa succede se scegliamo un tipo float? Stesse dimensioni di 32 bit, ma ora hai ben 24 bit di precisione, per tutti gli oggetti. Ciò significa che l'orologio ha una precisione sufficiente per rimanere in sincronia fino ai secondi per anni. I proiettili non hanno una precisione più elevata, ma "vivono" solo per frazioni di secondo, quindi sarebbe assolutamente inutile se avessero. E non ti trovi in ​​nessun tipo di problema se vuoi descrivere oggetti anche molto più veloci (perché non la velocità della luce? Nessun problema) o molto più lenti. Certamente non avrai bisogno di queste cose in un gioco, ma a volte lo fai nelle simulazioni fisiche.

E con i numeri a virgola mobile, ottieni sempre la stessa precisione e senza prima dover scegliere abilmente un raster non ovvio. Questo è forse il punto più importante, poiché tali necessità di scelta sono soggette a errori.


I numeri interi sono perfettamente precisi. L'inaccuratezza dipende da un calcolo errato.
fjdumont,

15
I numeri interi sono perfettamente precisi solo quando li usi per rappresentare effettivamente numeri interi (ℤ). Rappresentare qualcos'altro significa, in effetti, un calcolo errato. In tal caso, hai due possibilità: definire un tipo che si adatti perfettamente ai numeri che si desidera effettivamente rappresentare. questo è possibile, per esempio Mathematica può farlo. Ma è molto complicato e dispendioso in termini di tempo, e di solito non vale la pena perché in realtà non hai bisogno di una precisione perfetta. Ma hai bisogno di una buona precisione, ed è qui che i float generalmente fanno un lavoro migliore degli interi.
lasciato circa il

53

Li usi quando descrivi un valore continuo piuttosto che uno discreto . Descrivere non è più complicato di così. Basta non commettere l'errore di assumere che un valore con un punto decimale sia continuo. Se cambia tutto in una volta in pezzi, come aggiungere un centesimo, è discreto.


28

Hai davvero due domande qui.

Perché qualcuno ha bisogno della matematica in virgola mobile, comunque?

Come sottolinea Karl Bielefeldt, i numeri in virgola mobile consentono di modellare quantità continue - e le trovi ovunque - non solo nel mondo fisico, ma anche in luoghi come affari e finanza.

Ho usato la matematica in virgola mobile in molte, molte aree della mia carriera di programmazione: chimica, lavorando su AutoCAD e persino scrivendo un simulatore Monte Carlo per fare previsioni finanziarie. In effetti, c'è un ragazzo di nome David E. Shaw che ha utilizzato a Wall Street tecniche di modellazione scientifica basate su virgola mobile per guadagnare miliardi.

E, naturalmente, c'è la computer grafica. Mi consulto nello sviluppo di eye candy per le interfacce utente e provando a farlo al giorno d'oggi senza una solida comprensione di virgola mobile, trigonometria, calcolo e algebra lineare, sarebbe come presentarsi a uno scontro a fuoco con un coltellino.

Perché qualcuno dovrebbe aver bisogno di un float contro un doppio ?

Con le rappresentazioni standard IEEE 754, un float a 32 bit offre circa 7 cifre decimali di precisione ed esponenti nell'intervallo da 10 -38 a 10 38 . Un doppio a 64 bit fornisce circa 15 cifre decimali di precisione ed esponenti nell'intervallo da 10 -307 a 10 307 .

Potrebbe sembrare che un galleggiante sarebbe sufficiente per ciò di cui qualcuno avrebbe ragionevolmente bisogno, ma non lo è. Ad esempio, molte quantità del mondo reale sono misurate in più di 7 cifre decimali.

Ma più sottilmente, c'è un problema colloquialmente chiamato "errore di arrotondamento". Le rappresentazioni binarie in virgola mobile sono valide solo per valori le cui parti frazionarie hanno un denominatore che ha una potenza di 2, come 1/2, 1/4, 3/4, ecc. Per rappresentare altre frazioni, come 1/10, devi "arrotondare" il valore alla frazione binaria più vicina, ma è un po 'sbagliato - questo è "errore di arrotondamento". Quindi, quando fai matematica su quei numeri inaccurati, le imprecisioni nei risultati possono essere molto peggiori di quelle che hai iniziato - a volte le percentuali di errore si moltiplicano o addirittura si accumulano in modo esponenziale.

Comunque, più cifre binarie devi lavorare, più la tua rappresentazione binaria arrotondata sarà vicina al numero che stai cercando di rappresentare, quindi il suo errore di arrotondamento sarà minore. Quindi, quando fai matematica su di esso, se hai molte cifre con cui lavorare, puoi fare molte più operazioni prima che l'errore di arrotondamento cumulativo si accumuli fino a dove è un problema.

In realtà, i 64 bit raddoppiano con le loro 15 cifre decimali non sono sufficienti per molte applicazioni. Stavo usando numeri a virgola mobile a 80 bit nel 1985 e IEEE ora definisce un tipo a virgola mobile a 128 bit (16 byte), per il quale posso immaginare gli usi.


2
+1 Bob La mia esperienza con i sistemi di controllo ad alta risoluzione come il telescopio per l'astronomia è che i doppi a 64 bit non sono abbastanza buoni se non si ordinano i termini. Idem per il controllo del fuoco e la navigazione a lungo raggio
Tim Williscroft,

20

È un'idea sbagliata comune, che ovunque tu abbia a che fare con il denaro, dovresti archiviarne il valore come numero intero (centesimi). Mentre in alcuni casi semplici come il negozio online è vero, se hai qualcosa di più avanzato non aiuta molto.

Facciamo un esempio: uno sviluppatore guadagna $ 100.000 all'anno. Qual è lo stipendio del suo mese esatto? Usando il numero intero ottieni $ 8333,33 (¢ 833333), che moltiplicato per 12 è $ 99.999,96. Mantenerlo come numero intero è stato di aiuto? No, no.

Le banche usano sempre valori decimali / interi? Bene, lo fanno per la parte transazionale. Ad esempio, non appena si inizia a parlare di investimenti bancari, ad eccezione di tenere traccia delle transazioni effettive, tutto il resto è fluttuante. Dal momento che è tutto codice interno, non lo vedrai, ma puoi fare un picco su QuantLib , che è essenzialmente lo stesso (tranne molto più pulito ;-).

Perché usare i galleggianti? Perché l'uso del decimale non aiuta affatto quando si usano funzioni come radice quadrata, logaritmi, potenze con esponenti non interi ecc. E naturalmente i float sono molto più veloci dei tipi decimali.


1
@Job - decimali e float sono molto diversi. È possibile memorizzare 0,1 esattamente in un tipo decimale, ma non in un float o double.
Scott Whitlock,

3
Ho avuto un'altra domanda. Se hai pagato $100,000/12e utilizzato un galleggiante. Perché il risultato dovrebbe essere esattamente $ 100.000? Perché il float (o il decimale) non viene arrotondato per eccesso o per difetto ogni volta che qualcuno viene pagato? Sto parlando di quando si scrive un assegno (non si può fare 1/2 o 1/3 un centesimo) o un deposito diretto (presumo che abbia gli stessi limiti)

@acido: >>> x = 100000 / 12.0 >>> x * 12
100000,0

rileggi il mio commento? la mia domanda è quando uso il software per creare un controllo ogni mese. Dal momento che uno non può pagare 1/2 centesimo come ottiene la persona l'intero importo dopo un anno?

2
@acido: non è possibile utilizzare la divisione lineare, indipendentemente dal fatto che si utilizzino numeri interi, decimali o dividi come float e poi round. Questo è il punto, usare il decimale non aiuta in quel caso.
Vartec,

4

Quello che hai descritto è un ottimo modo per aggirare situazioni in cui controlli tutti gli ingressi e le uscite .

In realtà non è così. Dovrai essere in grado di far fronte a sistemi che ti forniscono i loro dati come un valore reale con un certo grado di precisione e ti aspetteresti di restituire i dati nello stesso formato. In questi casi si dovrà incontrare questi problemi.

In effetti incontrerai questi problemi anche se usi i trucchi che hai elencato. Quando si calcola un'imposta del 17,5% su un prezzo, si ottengono centesimi frazionari se si memorizza il valore in dollari o centesimi. Devi ottenere l'arrotondamento corretto poiché il fisco si arrabbia molto se non lo paghi abbastanza. L'uso dei moneytipi corretti (qualunque essi siano nella lingua che stai usando) ti salverà da un mondo di dolore.


Qual è il tipo di denaro? (lingua o link di riferimento) e perché è il tipo "corretto"? È perché è ... 128 bit o più? L'altro mio perché usare i miei "trucchi" non sarebbe corretto? Hai un numero intero in centesimi. Se lo moltiplichi per 0,175 otterrai un numero intero e lo userai per quello che vuoi. Pensando al tuo esempio, penso che float sarebbe in grado di mantenere il mio valore con sufficiente precisione, ma non dovrò preoccuparmi che 0.3f == 0.3d sia falso. -edit- e +1

1
@ acidzombie24 - Non intendevo un tipo specifico, ma qualunque tipo di linguaggio usi per rappresentare i valori monetari. Inoltre, se hai 10 centesimi e moltiplichi per 0,175 hai 1,75 centesimi: come gestisci questo con l'aritmetica intera? È 1 centesimo o 2 centesimi? Sbagli e il tuo cliente potrebbe finire per possedere molti soldi con il fisco .
ChrisF

Non dovresti mai moltiplicare 10 (un numero intero) per 0,175 (un numero reale / mobile) perché non dovresti mescolare numeri esatti con numeri inesatti; il risultato sarà inesatto. In altre parole, in un sistema di numeri esatti, un valore come .175 non esisterebbe mai, e quindi questo è un calcolo non sensato. Una soluzione migliore è moltiplicare 10000 per 175 e inserire manualmente un punto decimale, se del caso.
Barry Brown,

8
@ Barry - Lo so. Stavo cercando di illustrare il tipo di problema che si presenta. Esiste anche un valore come 0,175 se l'aliquota fiscale è del 17,5% ed è necessario calcolare l'imposta su un articolo che costa 10 centesimi.
ChrisF

1
@acidzombie: il tipo corretto da usare per denaro è un decimale a virgola fissa con precisione elevata (almeno 4 decimali). Nessun se, e, o ma. Memorizzare i valori monetari come centesimi non è sufficiente, perché in pratica ti dà solo due punti di precisione.
Aaronaught,

3

"Dio ha creato i numeri interi, tutto il resto è opera dell'uomo." - Leopold Kronecker (1886).

Per definizione, non hai bisogno di altri tipi di numeri. La completezza di Turing per un linguaggio di programmazione si basa sulle semplici relazioni tra i vari tipi di numeri. Se riesci a lavorare con numeri interi (a / k / a numeri naturali), puoi fare qualsiasi cosa.

La domanda è piuttosto speciosa perché non ne hai bisogno . Forse vuoi luoghi dove è conveniente o ottimale o più economico o qualcosa del genere?


7
Possiamo anche fare a meno di numeri interi, dal momento che si possono anche costruirli usando solo le operazioni di teoria degli insiemi e l'insieme vuoto. Ma sia questo che argomentare dalla completezza di Turing sono riduzionismo accademico portato all'estremo.
Bob Murphy,

4
Inoltre, la completezza di Turing si applica solo al calcolo. Né numeri interi né razionali sono matematicamente completi, poiché nessuno dei due è chiuso alla convergenza delle sequenze di Cauchy. Quindi Kronecker era pieno di aria calda: se vuoi uno spazio metrico completo che includa i numeri interi, devi diventare reale: xkcd.com/849
Bob Murphy,

1
@Bob Murphy: "il riduzionismo accademico portato all'estremo". Precisamente. La domanda è scarsa e porta a questa possibile risposta.
S. Lott,

2

In una frase, i tipi decimali in virgola mobile incapsulano la conversione da e verso valori interi (che è tutto ciò che il computer sa come gestire a livello binario; non esiste un punto decimale in binario) che fornisce un logico, generalmente facile da- comprendere l'interfaccia per i calcoli dei numeri decimali.

Francamente, dire che non hai bisogno di float perché sai come fare matematica decimale usando numeri interi è come dire che sai come fare l'aritmetica a mano libera, quindi perché usare una calcolatrice? Quindi conosci il concetto; Bravo. Non significa che devi esercitare tale conoscenza per tutto il tempo. Spesso è più veloce, più economico e più comprensibile per un mago non binario dire semplicemente 3,5 + 4,6 = 8,1 piuttosto che convertire i fichi in una quantità intera.


1

Il vantaggio principale dei tipi a virgola mobile è che dal punto di vista del runtime, due o tre formati (vorrei che più lingue supportassero i formati a 80 bit) saranno sufficienti per la maggior parte degli scopi computazionali. Se i linguaggi di programmazione potessero facilmente supportare una famiglia di tipi a virgola fissa, la complessità hardware richiesta per un determinato livello di prestazioni sarebbe spesso inferiore con tipi a virgola fissa rispetto a virgola mobile. Sfortunatamente, fornire tale supporto è tutt'altro che "facile".

Affinché un linguaggio di programmazione soddisfi in modo efficiente il 98% delle esigenze numeriche delle applicazioni, dovrebbe includere dozzine di tipi e fornire operazioni definite per quelle che possono essere centinaia di combinazioni; inoltre, anche se un linguaggio di programmazione avesse un meraviglioso supporto in virgola fissa, alcune applicazioni dovrebbero comunque mantenere una precisione relativa approssimativamente costante su un intervallo sufficientemente ampio da richiedere il virgola mobile. Dato che la matematica in virgola mobile sarà necessaria in alcune occasioni in ogni caso, avendo i fornitori di hardware focalizzati sulle prestazioni matematiche con due o tre formati in virgola mobile e facendo in modo che il codice utilizzi questi formati ogni volta che funzionano ragionevolmente bene, generalmente otterranno risultati migliori "bang for the buck" di quello che cercherebbe di ottimizzare il comportamento della matematica a virgola fissa.

Per inciso, la matematica a virgola fissa era più vantaggiosa con i processori a 8 e 16 bit che con quelli a 32 bit. Su un processore a 8 bit, in una situazione in cui 32 bit non sarebbero abbastanza sufficienti, un tipo a 40 bit costerebbe solo il 25% in più di spazio e il 25-50% in più di tempo rispetto al tipo a 32 bit e richiederebbe il 37,5% meno spazio e il 37,5-60% in meno di tempo rispetto a un tipo a 64 bit. Su una piattaforma a 32 bit, se un tipo a 32 bit non è sufficiente per qualcosa, spesso ci sono pochi motivi per usare qualcosa di meno di 64 bit. Se un tipo a virgola fissa a 48 bit sarebbe adeguato, un "doppio" a 64 bit funzionerà esattamente come il tipo a virgola fissa.


0

In generale, dovresti stare molto attento a usarli. Comprendere la perdita di precisione che può derivare da calcoli anche semplici è una sfida. Ad esempio, la media di un elenco di numeri come questo è una pessima idea:

double average(List<Double> data) {
  double ans = 0;
  for(Double d : data) {
    ans += d;
  }
  return ans / data.size();
}

Il motivo è che, per elenchi sufficientemente grandi, perdi sostanzialmente tutti i punti dati quando ansdiventa abbastanza grande (vedi ad esempio questo ). Il problema con questo codice è che per le piccole liste probabilmente funzionerà semplicemente --- è solo su larga scala che si rompe.

Personalmente, penso che dovresti usarli solo quando: a) il calcolo deve essere veramente veloce; b) non ti interessa che il risultato sia probabilmente lontano (a meno che tu non sappia davvero cosa stai facendo).


-1

Un pensiero è che useresti il ​​float o le doppie rappresentazioni quando devi trattare valori al di fuori dell'intervallo intero.

Le architetture odierne (approssimativamente) hanno un intervallo intero con segno di +/- 2.147.483.647 (32 bit) o ​​+/- 9.223.372.036.854.775.807 (64 bit). Unsigned lo estende di un fattore 2.

I galleggianti IEEE 754 (approssimativamente) vanno da +/- 1,4 × 10 ^ −45 a 3,4 × 10 ^ 38. Double estende tale intervallo fino a +/- 5 × 10−324 ± 2.225 × 10 ^ −308 con molte condizioni e specifiche omesse qui.

Naturalmente, la ragione più sorprendentemente ovvia è che potresti dover rappresentare -0 ;-)


Numeri principalmente da articoli di Wikipedia e sono pensati per essere illustrativi. Tranne -0, è solo per divertimento.
Stephen,

Il problema è che ci sono MOLTI numeri interi in quell'enorme intervallo che non sono affatto rappresentati.
Barry Brown,

@BarryBrown Assolutamente giusto. "molte condizioni e specifiche omesse" però.
Stephen,

-1

Il solito motivo è perché sono veloci poiché la JVM utilizza in genere il supporto hardware sottostante (a meno che non si utilizzi strictfp).

Vedi https://stackoverflow.com/questions/517915/when-to-use-strictfp-keyword-in-java per ciò che implica rigorfp.


La matematica a virgola mobile è più veloce della matematica a numeri interi? Su quale processore i calcoli in virgola mobile richiedono meno cicli rispetto ai calcoli di numeri interi?
Questo

1
@ this.josh, dipende fortemente dal numero di cifre che hai nei tuoi numeri. Inoltre, i numeri interi non possono essere divisi in modo preciso, il che può essere o non essere importante.

-2

Ecco perché abbiamo bisogno di sistemi operativi a 256 bit.

La lunghezza della plancia (la distanza più piccola che puoi misurare) = 10 ^ -35m
L'universo osservabile è 14Bn parsecs su = 10 ^ 25m
Quindi puoi misurare qualsiasi cosa in unità della lunghezza della plancia come numeri interi se hai solo 200 bit di precisione.


2
-1: e se simulassi le cose su una scala più ampia dell'universo osservabile?
amara,

2
@sparkleshy, ecco a cosa servono i puntatori FAR!
Martin Beckett,
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.