In .NET, why does System.Math.Round(1.035, 2, MidpointRounding.AwayFromZero)
yield 1.03 instead of 1.04? I feel like the answer to my question lies in the sect
Your suspicion is exactly right. Numbers with fractional portion, when expressed as literals in .NET, are by default doubles. A double (like a float) is an approximation of a decimal value, not a precise decimal value. It is the closest value that can be expressed in base-2 (binary). In this case, the approximation is ever so vanishingly on the small side of 1.035. If you write it using an explicit Decimal it works as you expect:
Console.WriteLine(Math.Round(1.035m, 2, MidpointRounding.AwayFromZero));
Console.ReadKey();
To understand why doubles and floats work the way they do, imagine representing the number 1/3 in decimal (or binary, which suffers from the same problem). You can't- it translates to .3333333...., meaning that to represent it accurately would require an infinite amount of memory.
Computers get around this using approximations. I'd explain precisely how, but I'd probably get it wrong. You can read all about it here though: http://en.wikipedia.org/wiki/IEEE_754-1985
I believe the example you're referring to is a different issue; as far as I understand they're saying that 0.1 isn't stored, in float, as exactly 0.1, it's actually slightly off because of how floats are stored in binary. As such let's suppose it actually looks more like 0.0999999999999 (or similar), something very, very slightly less than 0.1 - so slightly that it doesn't tend to make much difference. Well, no, they're saying: one noticeable difference would be that adding this to your number and rounding would actually appear to go the wrong way because even though the numbers are extremely close it's still considered "less than" the .5 for rounding.
If I misunderstood that page, I hope somebody corrects me :)
I don't see how it relates to your call, though, because you're being more explicit. Perhaps it's just storing your number in a similar fashion.
I'ts because the BINARY representation of 1.035 closer to 1.03 than 1.04
For better results do it this way -
decimal result = decimal.Round(1.035m, 2, MidpointRounding.AwayFromZero);
At a guess I'd say that internally 1.035 can't be represented in binary as exactly 1.035 and it's probably (under the hood) 1.0349999999999999, which would be why it rounds down.
Just a guess though.
The binary representation of 1.035d is 0x3FF08F5C28F5C28F, which in fact is 1.03499999999999992006394222699E0, so System.Math.Round(1.035, 2, MidpointRounding.AwayFromZero) yield 1.03 instead of 1.04, so it's correct.
However, the binary representation of 4.005d is 0x4010051EB851EB85, which is 4.00499999999999989341858963598, so System.Math.Round(4.005, 2, MidpointRounding.AwayFromZero) should yield 4.00, but it yield 4.01 which is wrong (or a smart 'fix'). If you check it in MS SQL select ROUND(CAST(4.005 AS float), 2), it's 4.00 I don't understand why .NET apply this 'smart fix' which makes things worse.
You can check binary representation of a double at: http://www.binaryconvert.com/convert_double.html