My boss reported my a bug today because my configuration was decreasing whenever he wanted to specify a value below 5%. I know that I can just round my number before casting it as int to fix my problem, but I don’t understand why this problem occurs.
I have a app.config file with the value “0.04” and a configuration section with a float property. When the section is read, the float value retrieved is 0.04, which is fine. I want to put this value in a windows forms TrackBar which accept an integer value so I multiply my value by 100 and a cast it as int. For some reason, the result is not 4, but it’s 3. You can test it like this :
Console.WriteLine((int)(float.Parse("0.04", System.Globalization.CultureInfo.InvariantCulture) * 100)); // 3
What happened?
It’s because 0.04 can’t be exactly represented as a
float– and neither can the result of multiplying it by 100. The result is very slightly less than 4, so the cast to int truncates it.Basically, if you want to use numbers represented accurately in decimal, you should use the
decimaltype instead offloatordouble. See my articles on decimal floating point and binary floating point for more information.EDIT: There’s something more interesting going on here, actually… in particular, if you assign the result to a local variable first, that changes the result:
I’m not sure exactly what’s going on here, but the exact value of 0.04f is:
… so it does make sense for it not to print 4, potentially.
I can force the result of 3 if the multiplication by 100 is performed with
doublearithmetic instead offloat:… but it’s not clear to me why that’s happening in the original version, given that
float.Parsereturnsfloat, notdouble. At a guess, the result remains in registers and the subsequent multiplication is performed usingdoublearithmetic (which is valid according to the spec) but it’s certainly a surprising difference.