18

I have encountered a weird case in Math.Round function in VB.Net

Math.Round((32.625), 2)

Result : 32.62

Math.Round((32.635), 2)

Result : 32.64

I need 32.63 but the function is working in different logic in these cases.

I can get the decimal part and make what I want doing something on it. But isn't this too weird, one is rounding to higher, one is rounding to lower.

So how can I get 32.63 from 32.625 without messing with decimal part ? (as the natural logic of Maths)

Mtok
  • 1,600
  • 11
  • 36
  • 62
  • my guess it has to do with the representation of floating point numbers, so 63.625 might be actually 63.6249999... – thumbmunkeys Feb 12 '13 at 14:43
  • But I'm writing it just there "63.625". so it can not be "63.624999" am I wrong ? – Mtok Feb 12 '13 at 14:44
  • 2
    You are, unfortunately, wrong - the number looks like 32.625 but internally it is stored as an Float-value, which is imprecise. Use a decimal datatype for high precision, but slow performance. – Christian Sauer Feb 12 '13 at 14:48
  • 6
    @ChristianSauer - In this case, 63.625 is exactly representable as a float. The problem is the rounding method as pointed out by Steven in his answer. – Chris Dunaway Feb 12 '13 at 17:12
  • Possible duplicate of [Why does .NET use banker's rounding as default?](http://stackoverflow.com/questions/311696/why-does-net-use-bankers-rounding-as-default) – p.s.w.g May 13 '16 at 13:16

5 Answers5

41

Math.Round uses banker's rounding by default. You can change that by specifying a different MidPointRounding option. From the MSDN:

Rounding away from zero

Midpoint values are rounded to the next number away from zero. For example, 3.75 rounds to 3.8, 3.85 rounds to 3.9, -3.75 rounds to -3.8, and -3.85 rounds to -3.9. This form of rounding is represented by the MidpointRounding.AwayFromZero enumeration member. Rounding away from zero is the most widely known form of rounding.

Rounding to nearest, or banker's rounding

Midpoint values are rounded to the nearest even number. For example, both 3.75 and 3.85 round to 3.8, and both -3.75 and -3.85 round to -3.8. This form of rounding is represented by the MidpointRounding.ToEven enumeration member.

Rounding to nearest is the standard form of rounding used in financial and statistical operations. It conforms to IEEE Standard 754, section 4. When used in multiple rounding operations, it reduces the rounding error that is caused by consistently rounding midpoint values in a single direction. In some cases, this rounding error can be significant.

So, what you want is:

Math.Round(32.625, 2, MidpointRounding.AwayFromZero)
Math.Round(32.635, 2, MidpointRounding.AwayFromZero)

As others have mentioned, if precision is important, you should be using Decimal variables rather than floating point types. For instance:

Math.Round(32.625D, 2, MidpointRounding.AwayFromZero)
Math.Round(32.635D, 2, MidpointRounding.AwayFromZero)
Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
  • I think the story is a bit more complicated than described in this answer. Note that thanks to the usual what-you-see-is-not-what-you-get nature of binary floating-point, `32.635` is _not_ a midpoint value, so it's unaffected by the midpoint rounding rule. (In Python 3, `round(32.635, 2)` gives (the nearest float to) `32.63`, even though Python _is_ using banker's rounding, and the `round` operation is correctly rounded.) It appears that VB.Net is (deliberately or not) _not_ doing correct rounding in this case: if it were, it would have given `32.63` regardless of midpoint rounding mode. – Mark Dickinson May 13 '20 at 16:51
4

Try this (from memory):

Math.Round((32.635), 2, MidPointRounding.AwayFromZero)

Mr47
  • 2,655
  • 1
  • 19
  • 25
2

Try this.

Dim d As Decimal = 3.625
    Dim r As Decimal = Math.Ceiling(d * 100D) / 100D
    MsgBox(r)

This should do what you want.

Richard Sites
  • 186
  • 2
  • 9
  • sorry, I edited my answer. I need to get 32.63 from 32.625 without messing with decimal part ? – Mtok Feb 12 '13 at 14:47
  • Okay, that changes things. – Richard Sites Feb 12 '13 at 14:52
  • Edited my answer to suit your needs based on that information. Hope it helps. I used the msgbox for testing, you can take it out. Sorry. It's a short way to do it. In fact you should make a function out of it. – Richard Sites Feb 12 '13 at 14:59
2

Hers a quick function you can add to simplify your life and make it so you don't have to type so much all the time.

 Private Function roundd(dec As Decimal)
    Dim d As Decimal = dec
    Dim r As Decimal = Math.Ceiling(d * 100D) / 100D
    Return r
End Function

Add this to your application then use the function

roundd(3.624)

or whatever you need.

to display the result - example

msgbox(roundd(3.625))

This will display a messagebox with 3.63

Textbox1.text = roundd(3.625)

this will set textbox1.text - 3.63 etc. etc. So if you need to round more then one number, it won't be so tedious and you can save alot of typing.

Hope this helps.

Richard Sites
  • 186
  • 2
  • 9
-1

You can't using floats which is what numbers like 32.625 is treated as in VB.Net. (There is also the issue of Banker's rounding as mention by @StevenDoggart - you are probably going to have to deal with both issues.)

The issue is that the number stored is not exactly what is entered because these numbers do not into a fixed binary representation e.g. 32.625 is stored as 32.62499997 and 32.635 as 32.63500001.
The only way to be exact is to store the numbers as the type Decimal

DIM num as Decimal
num = ToDecimal("32.625")
mmmmmm
  • 32,227
  • 27
  • 88
  • 117
  • 1
    In VB.NET, you can simply use the `D` suffix to specify a `Decimal` literal (e.g. `32.625D`) – Steven Doggart Feb 12 '13 at 15:00
  • How are you getting the value 32.62499997 from 32.625? 32.625 should be exactly representable as a float or double. The value I get (using code from Jon Skeet's site) for **32.635** is 63.63499999999999801048033987171947956085205078125. – Chris Dunaway Feb 12 '13 at 17:19
  • CPUs differ in the way of representing/processing/converting floats - why it is a bad idea to store money as a float datatype. Try it on many different processors, and you'll see the difference (double is also a float and you'll get spurious results - these datatypes are from the olden days of computing and should probably be avoided unless speed is of the essence) – MC9000 May 29 '18 at 04:33