*by Jim Karabatsos - GUI Computing*

Just when you thought you had most of Visual Basic's wrinkles mapped out, another one comes along and bites you when you aren't looking. Ross brought this one to my attention with a rather cryptic email:

*>> Since when is 49.5 \ 1 = 50?*

He was trying to figure out where he had gone wrong in his accounting calculations, but had come across a statement that evaluated as above.

Nonsense, I thought. I *know* that the "\" operator causes an INTEGER division -- it causes its operands to be converted to integers (or longs) first, THEN it does non-floating-point division. So Int(49.5) = 49, and therefore 49\1 = 49. Right?

Wrong.

Int(49.5) is indeed 49, but that's not what happens. Instead, CInt() is used. No difference, you say? Hah! In fact, Int() and CInt() are very different animals indeed. Int() truncates any fractional component of its argument towards the next lower integer.

Int(48.8) = 48 Int(48.5) = 48 Int(49.5) = 49 Int(48.2) = 48 Int(-48.8) = -49 Int(-48.5) = -49 Int(-49.5) = -50 Int(-48.2) = -49

On the other hand, CInt() is a rounding function and performs what is known as Banker's rounding. Banker's rounding was developed as a means of making rounding a neutral exercise. If you think about it, any fractional component has an odd number of possible values in a decimal numbering system. For example, if we are rounding cents to dollars, there are 99 values that are rounded, 1 to 99 cents. The exact dollar amount requires no rounding. This means that, if we assume that all possible values occur with equal probability, we cannot come up with any rounding method that rounds up and down with equal frequency. The traditional, simple rounding rule is everything less than one half rounds down, whereas everything from one half and up rounds up. If we use our dollar example again, this means that 49/99 times we will round dowm, but 50/99 times we will round up, with a net 1/99 unit bias towards rounding up.

If you're a bank, that REALLY adds up. So what they decided to do was to link the rounding of the mid-point value to the last, non-rounding digit. If that digit is even, the mid-point is rounded towards zero; conversely when it is odd, the mid-point is rounded away from zero.

Notice I did not say "up" or "down". CInt() treats negative numbers in a different manner to Int(), so keep this in mind. Here are the same examples using CInt() alongside the previous Int() examples for comparison:

Int(48.8) = 48 |
CInt(48.8) = 49 |

Int(48.5) = 48 |
CInt(48.5) = 48 |

Int(49.5) = 49 |
CInt(49.5) = 50 |

Int(48.2) = 48 |
CInt(48.2) = 48 |

Int(-48.8) = -49 |
CInt(-48.8) = -49 |

Int(-48.5) = -49 |
CInt(-48.5) = -48 |

Int(-49.5) = -50 |
CInt(-49.5) = -50 |

Int(-48.2) = -49 |
CInt(-48.2) = -48 |

As an aside, you might call me a cynic, but have you noticed how many monetary amounts end in ODD digits, especially nine? Coincidence, you say? Hmmm.

So, that's CInt() in a nutshell. CInt() is used a LOT in VB behind the scenes. If you assign a floating-point (or currency) variable to an integer or long, the CInt() logic is used. It is also used whenever a non-integral intermediate result in an expression is converted to an integral value -- a pretty rare occurrance, I'll grant you, but hey, WE got bitten!

The same logic is NOT used by Format$(), as demonstrated by the following:

Print Format$(48.5,"#0") ' Prints 49 Print Format$(48.2,"#0") ' Prints 48 Print Format$(49.5,"#0") ' Prints 50

Here, traditional rounding is being done, not banker's rounding. Isn't consistency wonderful, in all its forms?

By the way, once we had nutted out what was going on, it was a simple matter to take over from VB and do things how we wanted them done. We just explicitly used Int() to coerce the variables the way we wanted them, as in :

49.5 \ 1 = 50 Int(49.5) \ 1 = 49

All's well that ends well.

April '98