14
        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 :

   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   //??

Why the value differs sometimes while doing Explicit Cast , but not always. Any reason ?

C-va
  • 2,910
  • 4
  • 27
  • 42

5 Answers5

9

To take one example, 21.65 in float format is actually represented by a number like 21.6499999. The Convert.ToInt32 rounds the number to the nearest integer, yielding 21.65, while the explicit cast (Int32) just truncates (rounding toward zero), so you get 21.64.

If you want the floating point numbers to be represented in the computer the same way they look printed out, use decimal instead of float or double.

Gabe
  • 84,912
  • 12
  • 139
  • 238
  • Thanks for the reply Gabe. But why it is not in all scenario. For eg. Convert: 4511 ExplCast: 4511 // OK Convert: 2222 ExplCast: 2221 //?? – C-va Jun 29 '12 at 12:28
  • 1
    22.22 cannot be encoded exactly as a floating point number. When you multiply by 100, you end up with a value something like 2221.999999 – spender Jun 29 '12 at 12:37
  • Round-to-even is the default `.Round` behavior in .NET isn't it? See http://stackoverflow.com/questions/311696/why-does-net-use-bankers-rounding-as-default – Kit Roed Jun 29 '12 at 12:40
  • @MSK (and others): It is very interesting to notice that if you try this on a 64-bit machine, with exactly these numbers, and if you compile your code with the **x64** "Solution Platform" setting, then this behavior goes away! In that case the compiler really does the multiplication with 64-bit numbers ("double precision", not "single"), and only after that converts to `float`/`Single`. Of course, the same kind of behavior will also arise with double precision if you choose your numbers differently. But here's an example where a program does something else when compiled for x64. – Jeppe Stig Nielsen Sep 27 '12 at 14:27
4

Convert.ToInt32 rounds to the nearest integer, the direct cast just truncates the number.

So, if you, due to floating point imprecision, have a value of 2165.99999whatever instead of 2165.0, the direct cast truncates all after the floating point, while Convert.ToInt32 rounds to the nearest integer.

Example:
22.22f * 100.0f results in something like 2221.99993133544921875.
So Convert.ToInt32 will round it up to the expected value 2222, while the cast will truncate it to 2221.

45.11f * 100.0f on the other hand results in about 4511.00006103515625,
which Convert.ToInt32 rounds down, which results in 4511, the same result as when casting directly.

Botz3000
  • 39,020
  • 8
  • 103
  • 127
2

Following from Botz3000 answer, Relevent sections from MSDN:

Convert.ToInt32 Method (Single)

Return Value Type: System.Int32 value, rounded to the nearest 32-bit signed integer. If value is halfway between two whole numbers, the even number is returned; that is, 4.5 is converted to 4, and 5.5 is converted to 6.

Explicit Numeric Conversions Table

•When you convert from a double or float value to an integral type, the value is rounded towards zero to the nearest integral value. If the resulting integral value is outside the range of the destination value, the result depends on the overflow checking context. In a checked context, an OverflowException is thrown, while in an unchecked context, the result is an unspecified value of the destination type.

Indy9000
  • 8,651
  • 2
  • 32
  • 37
1

I think you will find the problem is caused by inaccuracies with floating point precision, specifically using a float. You will find the issue disappears when using a decimal. There is a really good answer on the differences between decimal and double (and float) here: decimal vs double! - Which one should I use and when?

Community
  • 1
  • 1
Kane
  • 16,471
  • 11
  • 61
  • 86
  • Hi again @Kane. This is misleading. Try this: (int)((1m/3m)*300m) (hint, it's not 100). Decimals might be more appropriate for some kinds of numbers, binary floats for others, but both are susceptible to this kind of problem. (but in the case where decimal strings are parsed, decimal would indeed be the right way to go) – spender Jun 29 '12 at 12:49
1

Calling Convert.ToInt32 is like calling:

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

Direct Casting is like calling

(int) Math.Floor(float);

Floor always gives you a value less than or equal to the value you supply in the argument. Floating point representations are not "precise". So 21.65 is probably represented as 21.649999 or similar since there is not enough precision.

So: 21.65 *100 = 2164.9999 Flooring this value should give you an integer which is smaller than or equal to 2164.9 ... ie: 2164

Rounding 2164.99 on the other hand would give you: 2165

You can see the effect here:

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

Using doubles instead of float's (more precision, but still not infinite):

Console.WriteLine(Math.Round(21.65d*100));  //2165
Console.WriteLine(Math.Floor(21.65d*100));  //2165
Grynn
  • 1,254
  • 11
  • 18
  • Thanks for the reply. But why it is not in all scenario – C-va Jun 29 '12 at 12:39
  • Ah, it's because of the way floating point numbers are stored in memory. Some numbers can be precisely represented, some numbers cannot be. Loads of gory details here: http://en.wikipedia.org/wiki/Single_precision – Grynn Jun 29 '12 at 12:42
  • 1
    Try this page: http://www.binaryconvert.com/result_float.html?decimal=050049046054053 The best representation of 21.65 = 2.16499996185302734375E1 The best representation of 20.42 = 2.042E3 (nice and precise!) – Grynn Jun 29 '12 at 12:47