2012-06-29 14 views
13
 string[] strArray = new string[10] { "21.65", "30.90", "20.42", "10.00", "14.87", "72.19", "36.00", "45.11", "18.66", "22.22" }; 
     float temp = 0.0f; 
     Int32 resConvert = 0; 
     Int32 resCast = 0; 
     for (int i = 0; i < strArray.Length; i++) 
     { 
      float.TryParse(strArray[i], out temp); 
      resConvert = Convert.ToInt32(temp * 100); 
      resCast = (Int32)(temp * 100); 
      Console.WriteLine("Convert: " + resConvert + " ExplCast: " + resCast); 
     } 

Ans:Integer conversione in C#

Convert: 2165 ExplCast: 2164 // ?? 
    Convert: 3090 ExplCast: 3089 // ?? 
    Convert: 2042 ExplCast: 2042 
    Convert: 1000 ExplCast: 1000 
    Convert: 1487 ExplCast: 1486 //?? 
    Convert: 7219 ExplCast: 7219 
    Convert: 3600 ExplCast: 3600 
    Convert: 4511 ExplCast: 4511 
    Convert: 1866 ExplCast: 1865 //?? 
    Convert: 2222 ExplCast: 2221 //?? 

Perché il valore differisce a volte mentre si fa cast esplicito, ma non sempre. Qualche motivo?

+0

provare Int64 al posto di Int32 –

+0

@Rana: Qual è il problema con Int32? Qualsiasi motivo specifico? –

+1

R.S. Rana: È un'assurdità per valori così piccoli. – Joey

risposta

9

Per fare un esempio, il formato 21.65 nel formato float è in realtà rappresentato da un numero come 21.6499999. Il numero Convert.ToInt32 arrotonda il numero all'intero più vicino, con un risultato di 21,65, mentre il cast esplicito (Int32) tronca (arrotondando verso lo zero), quindi ottieni 21,64.

Se si desidera rappresentare i numeri in virgola mobile nel computer nello stesso modo in cui appaiono stampati, utilizzare decimal anziché float o double.

+0

Grazie per la risposta Gabe. Ma perché non è in tutti gli scenari. Per es. Convertire: 4511 ExplCast: 4511 // OK Conversione: 2222 ExplCast: 2221 // ?? –

+1

22.22 non può essere codificato esattamente come un numero in virgola mobile. Quando si moltiplica per 100, si finisce con un valore come 2221.999999 – spender

+0

Round-to-even è il comportamento predefinito '.Round' in .NET no? Vedi http://stackoverflow.com/questions/311696/why-does-net-use-bankers-rounding-as-default –

4

Convert.ToInt32rounds to the nearest integer, il cast diretto tronca il numero.

Quindi, se, a causa di imprecisioni in virgola mobile, ha un valore di 2165.99999whatever anziché 2165.0, il cast diretto tronca tutti dopo la virgola mobile, mentre Convert.ToInt32 giri al numero intero più vicino.

Esempio:
22.22f * 100.0f risultati in qualcosa di simile 2221.99993133544921875.
Quindi Convert.ToInt32 lo arrotonderà a fino al fino al valore atteso 2222, mentre il cast lo troncerà a 2221.

45.11f * 100.0f sugli altri risultati mano in merito 4511.00006103515625,
che Convert.ToInt32 giri giù, che si traduce in 4511, lo stesso risultato di quando colata direttamente.

+0

Grazie, non sapevo che – Kane

1

Penso che troverete il problema è causato da imprecisioni con precisione in virgola mobile, in particolare utilizzando un float. Il problema scompare quando si utilizza un decimal. C'è una risposta davvero buona sulle differenze tra decimale e doppio (e float) qui: decimal vs double! - Which one should I use and when?

+0

Ciao di nuovo @Kane. Questo è fuorviante. Prova questo: (int) ((1m/3m) * 300m) (suggerimento, non è 100). I decimali potrebbero essere più appropriati per alcuni tipi di numeri, i galleggianti binari per gli altri, ma entrambi sono suscettibili a questo tipo di problema. (ma nel caso in cui vengano analizzate le stringhe decimali, il decimale sarebbe davvero la strada giusta) – spender

2

seguito da Botz3000 risposta, sezioni relevent da MSDN:

Convert.ToInt32 Method (Single)

Valore restituito Tipo: System.Int32 valore, arrotondato a 32 bit intero con segno. Se il valore è a metà strada tra due numeri interi, viene restituito il numero pari ; cioè, 4,5 viene convertito in 4, e 5.5 è convertito 6.

Explicit Numeric Conversions Table

• Quando si converte da un valore doppio o float in un tipo integrale, il valore viene arrotondato verso zero al valore integrale più vicino.Se il valore integrale risultante è al di fuori dell'intervallo del valore di destinazione , il risultato dipende dal contesto di controllo dell'overflow. In un contesto controllato , viene generata una OverflowException, mentre in un contesto deselezionato, il risultato è un valore non specificato del tipo di destinazione.

1

Calling Convert.ToInt32 è come chiamare:

(int) Math.Round(floatValue, 0);

Casting diretto è come chiamare

(int) Math.Floor(float);

piano si dà sempre un valore inferiore o uguale al valore fornisci nella discussione. Le rappresentazioni a virgola mobile non sono "precise". Quindi 21.65 è probabilmente rappresentato come 21.649999 o simile poiché non c'è abbastanza precisione.

Quindi: 21,65 * 100 = 2164,9999 Pavimentazione questo valore dovrebbe darvi un intero che è minore o uguale a 2164,9 ... cioè: 2164

A completare 2164,99 d'altra parte si darebbe: 2165

È possibile vedere l'effetto qui:

Console.WriteLine(Math.Round(21.65f*100)); //2165 
Console.WriteLine(Math.Floor(21.65f*100)); //2164 

Utilizzando doppie invece di del galleggiante (maggiore precisione, ma ancora non infinito):

012.
Console.WriteLine(Math.Round(21.65d*100)); //2165 
Console.WriteLine(Math.Floor(21.65d*100)); //2165 
+0

Grazie per la risposta. Ma perché non è in tutti gli scenari –

+0

Ah, è a causa del modo in cui i numeri in virgola mobile sono memorizzati in memoria. Alcuni numeri possono essere rappresentati con precisione, alcuni numeri non possono essere.Carichi di dettagli sanguinosi qui: http://en.wikipedia.org/wiki/Single_precision – Grynn

+1

Prova questa pagina: http://www.binaryconvert.com/result_float.html?decimal=050049046054053 La migliore rappresentazione di 21.65 = 2.16499996185302734375E1 La migliore rappresentazione di 20.42 = 2.042E3 (bello e preciso!) – Grynn