[the short answer is at the end]
I am having a rounding problem in a value i am trying to display in VB.NET.
If I have the following code:
Dim x As Single = 2726.795
Dim y As Single = Math.Round(2726.795, 2)
Dim s as String = String.Format("{0:0.00}", y)
The value of s is 2726.79
If I have the following code:
Dim y As Single = Math.Round(2726.795, 2)
Dim s as String = String.Format("{0:0.00}", y)
The value of s is 2726.80
The two lines of the second example are the same as the second two
lines of the first example. I suspect you have a typo somewhere.
Incidentally, I see you are running without the benefit of Option
Strict (Math.Round returns a Double which cannot be implicitly
converted to a Single). I suggest turning it on - the additional code
sometimes required is a small price to pay for the increased
compile-time checking.
My guess is that you meant the first example to be:
Dim x As Single = 2726.795
Dim y As Single = Math.Round(x, 2)
Dim s As String = String.Format("{0:0.00}", y)
which *does* produce 2726.795.
Assuming that is the right correction:
Any ideas why the first example is not rounding to 2726.80?
The first parameter to the particular overload of Math.Round being used
is a Double. That means that when a Single is supplied (as in the
corrected first example), it must be converted to a Double. This is OK
to be done implicitly, since it is a widening conversion. Let's have a
look at the actual exact values involved:
Dim x As Single = 2726.795
Dim xAsDouble As Double = x
Console.WriteLine(x.ToString("G9"))
Console.WriteLine(xAsDouble.ToString("R"))
The output is
2726.79492
2726.794921875
So we see that trying to put 2726.795 into a Single actually puts in a
value slightly *less* than 2726.795, which is why it gets rounded to
....79. With a double, on the other hand (as in the second example):
Dim x As Double = 2726.795
Console.WriteLine(x.ToString("G17"))
2726.7950000000001
So trying to put 2726.295 into a Double actually puts in a value
slightly *more* than 2726.295, which is why it gets rounded to ...80.
That's the why. The next question is the what-to-do.
I'd say the first recommendation is this: don't use floating-point
types to deal with exact values. Actually it's more than a
recommendation, it's a golden rule. It's customary for me to give my
usual example at this point:
Debug.Print(0.1+0.1+0.1-0.3) 'guess output before running!
The next recommendation would be, don't use Single at all unless you
have a very good reason. Single only has about *seven* decimal digits
of precision, which we run into pretty quickly in the real world. Any
memory gains you think you might be getting by using Singles over
Doubles are almost certainly illusory. Any performance gains you think
you are getting are _wrong_: "Double is the most efficient of the
fractional data types, because the processors on current platforms
perform floating-point operations in double precision", to quote the
docs. In short, if you are going to use floating point (with the above
caveat that you shouldn't if... you shouldn't), use Double not Single.
And now for the short answer:
I need to use the first example as I read the value of x in as a string from
an xml file then convert it to a single in order to round and format it to 2
decimal places for a report display.
Use Decimal as the intermediate data type to avoid loss of precision.