Wednesday, February 13, 2008

Well Rounded - Rounding to fractional precision in VBScript

The challenge was to figure out an algorithm to round any given floating point number to the nearest quarter (0.25) precision. Once you figure out the mathematical formula, it is fairly simple to implement it in .Net. Implementing it in VBScript is another ball game altogether. It took me a while to figure out why the code that worked so well in VB.Net, gave me the dreaded "Divide by zero" error in VBScript. The offending element in the code is the "MOD" operator. Of course it had to behave differently in VBscript. Read the below snippet that Microsoft mentions in MSDN reference for the VBscript "MOD" operator.

"The modulus, or remainder, operator divides number1 by number2 (rounding floating-point numbers to integers) and returns only the remainder as result."

"rounding floating-point numbers to integers"? Why... that is so obvious. Why didn’t I think of that before? (ignore my lame attempt at sarcasm!) How can any computer language provide a mathematical operator that rounds its input numbers to integers before performing the operation? Apparently, Microsoft thought nobody uses the "MOD" operator, so no one would notice. Enough ragging on Microsoft and VBScript. Needless to say, the outcome of this is that if your divisor is less than 0.5, it will be rounded to zero before the MOD operation is performed (For example: 10.76 MOD 0.25 will be converted to 11 MOD 0). That's when the mystical "Divide by zero" error will appear.

Undeterred by this little setback, I decided to write my own algorithm for rounding to the nearest given fractional precision without using the infamous MOD operator. My application only required rounding a floating-point number to the nearest quarter (0.25) precision, but I tried to make it generic enough so it could be rounded to any fraction. Here is the small code snippet that I came up with...

RoundFraction = 0.25
HalfFraction = RoundFraction / 2
RoundDown = FloatNum - (Fix(FloatNum/RoundFraction) _
* RoundFraction)
RoundUp = (FloatNum + HalfFraction) _
- (Fix((FloatNum + HalfFraction)/RoundFraction) _
* RoundFraction)
If RoundDown > HalfFraction Then
Result = _
Round((FloatNum + HalfFraction) - RoundUp, 2)
Else
Result = Round(FloatNum - RoundDown, 2)
End If



It works like a charm every time. Comments or better code is always appreciated, so let me know what you think of it.










1 comment:

Eric Credeur said...

Thank you so much for this code snippet! It saved me a ton of time trying to figure it.