Can some more experienced guys explain this strange error I found today? I was getting strange amounts when I was loading table data with my C# script.
As it turns out the problem is a different output from similar functions:
string amount_in_string = "1234567,15";
double amount_in_double = double.Parse(amount_in_string);
float amount_in_float = float.Parse(amount_in_string);
//amount_in_double = 1234567.15;
//amount_in_float = 1234567.13;
Why do I get such a different result when float and double are similar types (floating point). Can the precision make a difference with small amounts like these?
3 Answers 3
When "1234567.15" is converted to double, the result is the closest value representable in double, which is 1234567.1499999999068677425384521484375. Although you report in the question that the value is 1234567.15, the actual value is 1234567.1499999999068677425384521484375. "1234567.15" would be displayed when the value is displayed with a limited number of decimal digits.
When "1234567.15" is converted to float, the result is the closet value representable in float, which is 1234567.125. Although you report the value is 1234567.13, the actual value is 1234567.125. "1234567.13" may be displayed when the value is displayed with a limited number of decimal digits.
Observe that 1234567 exceeds 1,048,576, which is 220. The 32-bit floating-point format used for float uses 24 bits for the significand (fraction portion of the number). If the high bit of the significand represents 220, the low bit represents 220−23 = 2−3 = 1⁄8. This is why you see "1234567.15" converted to a value rounded to the nearest eighth.
6 Comments
float, round to the nearest 1⁄8, as stated. For double, round to the nearest 2^(20-52) = 2^-32. The easiest way to do this rounding is to convert the number with double.Parse and then print it with 32 or more decimal digits after the decimal point. (I do not know whether C# will print so many digits accurately. Microsoft has not always provided good conversion routines. I used Apple’s developer tools for this, which do convert accurately. I used C: printf(%.99g\n", 1234567.15);.) If one wanted to look at the math in more detail, extended-precision software could help.Floating point numbers are never exact, they are representations of numbers. An example commonly used is think of
1/3 + 1/3 = 2/3
...so the answer in floating point numbers, .33333 + .33333, is not 2/3rds exactly, it is .66666.
Long story short, the more precise fraction you take that can't be converted to an exact binary is going to always have a rounding number. The more precise the more likely it will have rounding errors.
Keep in mind if you do multiple different fractions, you can even have multiple different rounding errors that either make the number accidentally correct, or even further off.
6 Comments
, (culture), not exactly with the types used. Besides, multiplication can't be an answer: what will happen in the case of string ammount_in_string = 34028229999999999999999999999999999999.15? Multiplying by 10 will become an OverflowExceptionA fiddle you can see the results (there is a culture problem here too)
https://dotnetfiddle.net/Lnv1q7
string amount_in_string = "1234567,15"; // NOTE THE COMMA in original
string amount_in_string = "1234567.15"; //my correct culture
double amount_in_double = double.Parse(amount_in_string);
float amount_in_float = float.Parse(amount_in_string);
Console.WriteLine(amount_in_string);
Console.WriteLine(amount_in_double);
Console.WriteLine(amount_in_float);
Results (parsing in the incorrect culture!)
1234567,15
123456715
1.234567E+08
Results (parsing in the correct culture!)
1234567.15
1234567.15
1234567
Another one that demonstrates the loss of precision with float
float flt = 1F/3;
double dbl = 1D/3;
decimal dcm = 1M/3;
Console.WriteLine("float: {0} double: {1} decimal: {2}", flt, dbl, dcm);
Result
float: 0.3333333
double: 0.333333333333333
decimal: 0.3333333333333333333333333333
floats should only be used in cases were some loss of precision is not extremely valuable. This is because floats are 32bit where decimal is 128 bit floats are mainly used in pixel coordinates, and loss of precision doesn't matter because a the consumer translates locations to more precise coordinates any way.
in .NET floats "should" be actively avoided unless you don't care about loss of (some) precision. Which is probably never now a days (unless you writting games)
This is where the banking problem came from when 100th's of a penny/cent across transactions where lost, seemingly invisible per transaction but amounting to large amounts of "missing" money.
Use decimal
14 Comments
float.Parse. It is merely displayed as "1234567" because apparently the default formatting limits the digits shown. Printing more digits would show "1234567.13". So OP’s result is not due to a locale/culture mismatch but is due to floating-point precision.Explore related questions
See similar questions with these tags.
decimalfor this, notdoubleorfloat.!doubleandfloatare binary point types (for example, adoubleor afloatcan never be equal to 0.1 as is) anddecimalis a decimal (base 10) point type. If you're looking for point accuracy, you should be usingdecimalinstead!