2014-09-06 13 views
11

Ecco una parte di codice che non capisco:Perché un risultato di divisione differisce in base al tipo di getto?

byte b1 = (byte)(64/0.8f); // b1 is 79 
int b2 = (int)(64/0.8f); // b2 is 79 
float fl = (64/0.8f); // fl is 80 

Perché sono i primi due calcoli off per uno? Come devo eseguire questa operazione, quindi è veloce e corretta?

EDIT: avrei bisogno il risultato nel byte

+0

Ho modificato il titolo. Per favore vedi, "[Le domande dovrebbero includere" tag "nei loro titoli?] (Http://meta.stackexchange.com/questions/19190/)", dove il consenso è "no, non dovrebbero". –

+0

Questo articolo di Jon Skeet dovrebbe coprire la tua domanda: http://csharpindepth.com/articles/general/floatingpoint.aspx – Warlock

risposta

6

EDIT: Non del tutto corretto, vedi: Why does a division result differ based on the cast type? (Followup)

problema di arrotondamento: Con la conversione al byte/int, si sta clipping dei decimali.

Ma 64/0.8 non deve dare luogo a cifre decimali? Sbagliato: a causa della natura dei numeri in virgola mobile, 0.8f non può essere rappresentato esattamente come quello in memoria; è memorizzato come qualcosa vicino a 0.8f (ma non esattamente). Vedi Floating point inaccuracy examples o thread simili. Pertanto, il risultato del calcolo non è 80.0f, ma 79.xxx dove xxx è vicino a 1 ma non è ancora esattamente uno.

È possibile verificare questo digitando quanto segue nella finestra immediata in Visual Studio:

(64/0.8f) 
80.0 
(64/0.8f) - 80 
-0.0000011920929 
100 * 0.8f - 80 
0.0000011920929 

È possibile risolvere questo problema utilizzando l'arrotondamento:

byte b1 = (byte)(64/0.8f + 0.5f); 
int b2 = (int)(64/0.8f + 0.5f); 
float fl = (64/0.8f); 
+1

80.0f può essere rappresentato bene. 0,8f non può, tuttavia, in modo che in realtà non si ottenga 80.0f in primo luogo. – harold

+0

Grazie per averlo capito - ho risolto la mia risposta di conseguenza. – Matthias

+1

Si prega di controllare la mia domanda successiva: http://stackoverflow.com/questions/25703864/why-does-a-division-result-differ-based-on-the-cast-type-followup Ho ottenuto il binario per (64/0.8f) e ha funzionato all'indietro e in realtà equivale esattamente a 80. – ConditionRacer

3

Per capire il problema, è necessario capire le basi di floating point di rappresentanza e le operazioni.

0.8f non può essere rappresentato esattamente in memoria utilizzando un numero in virgola mobile.

In matematica, 64/0.8 uguale 80. In aritmetica in virgola mobile, 60/0,8 equivale approssimativamente 80.

Quando lanci un galleggiante per un intero o un byte, solo la parte intera del numero è tenuto. Nel tuo caso, il risultato impreciso della divisione in virgola mobile è leggermente inferiore a 80 quindi la conversione in intero produce 79.

Se hai bisogno di un risultato intero, ti suggerisco di arrotondare il risultato invece di lanciarlo . Un modo per farlo è quello di utilizzare la seguente funzione, che converte in un intero arrotondando al numero intero più vicino:

Convert.ToInt32(64/0.8f); 
5

ho paura rapida e corretta sono in disaccordo in casi come questo.

L'aritmetica in virgola mobile in binario genera quasi sempre piccoli errori dovuti a the underlying representation in our CPU architectures. Quindi nella tua espressione iniziale ottieni un valore un po 'più piccolo di quello matematicamente corretto. Se ti aspetti un numero intero come risultato di una particolare operazione matematica e ottieni qualcosa di molto vicino, puoi utilizzare il metodo Math.Round(Double, MidpointRounding) per arrotondare correttamente e compensare piccoli errori (e assicurati di scegliere the MidpointRounding strategy you expect).

semplicemente gettando il risultato a un tipo come ad esempio byte o int non fa arrotondamento - taglia semplicemente fuori la parte frazionaria (anche 1.99999f diventerà 1 quando hai appena lanci a questi tipi).

L'aritmetica decimale in virgola mobile è più lenta e richiede più memoria, ma non causa questi errori. Per eseguirlo, utilizza i valori letterali decimal anziché i valori letterali float (ad esempio 64/0.8m).

The rule of thumb è:

  • Se avete a che fare con la quantità esatte (tipicamente dall'uomo, come il denaro), utilizzare decimal.
  • Se si hanno a che fare con quantità inesatte (come costanti fisiche frazionali o numeri irrazionali come π), utilizzare double.
  • Se si hanno a che fare con quantità inesatte (come sopra) e una certa precisione può essere ulteriormente sacrificata per la velocità (come quando si lavora con la grafica), usare float.
Problemi correlati