Rounding Numbers and Round-off Errors

December 8 2002 at 12:13 PM

Solitaire (no login)

Some time ago I discovered that QBasic will round out numbers ending with .5 to the nearest EVEN integer, or any fractional number ending with 5 to the nearest EVEN least significant digit up to the limit of the type range. For example, if you declare a number AS INTEGER and assign it the value of 3.5, it will become 4, and if you assign it the value of 4.5, it will also become 4. Similarly, if you assign a Single (or default type) number the value of .12342335 it will print as .1234234 and .12342345 will also print as .1234234. The same will happen with default Single numbers 1234.2335 and 1234.2345. The CINT() function will also round out to the nearest even integer. Try this:

The same is true for the Round() function in Visual Basic 6. I was wondering if this was some kind of bug, until I checked the Internet and searched for Rounding Numbers. There seems to be a difference of opinion on how numbers ending with .5 are to be rounded. Most of us would round UP to the next highest number, as we were taught in mathematics classes. However, the preferred way is to round to the nearest EVEN number! I suppose this is used as the preferred method in order to avoid significant round-off errors. Look at it this way: Groups of numbers ending with 1 - 4 and groups of numbers ending with 6 - 9 would statistically be about the same. That leaves the odd group of numbers ending with the digit 5. If they were all to be rounded UP, then eventually, if thousands of numbers are totalled, The result would be too big, producing a large round-off error. On the other hand, if half of the numbers ending with 5 were rounded down and half rounded up, the error would not be as great. With individual or small groups of numbers, however, you would probably prefer to use the always round UP method.

I checked the Excel spreadsheet program to see how the rounding function worked there, and to my surprise, it always rounded UP if a number ended with 5. I decided to write a program with a function that will round out any number, positive or negative to any place selected by the user, using the round UP method same as in Excel. If the number is too big or too small for the largest data type, it will produce an error, so I included an errortrap to end the function if that happens. The code for this program appears below.

You should be aware that a round-off error may still occur if the results you require use the same number of places after the decimal as the number of places you have rounded out. Knowing how to round out a number is not the same as solving a round-off error; just the opposite. Eventually the missing fraction lost in multiple roundouts compounds the error and produces a result which is no longer accurate to the required degree. To avoid round-off errors, you must attempt to extend the number of decimal places as far as possible before rounding out.

The problem is that the computer is a finite machine and can only produce results to a finite degree of accuracy. After a certain number of decimal places have been reached within the memory limits of the data type, the computer rounds out the last digit automatically. Using a data type with the greatest range from minimum to maximum helps extend the accuracy. The SINGLE data type is accurate only to 7 significant digits. The DOUBLE data type is accurate to 15 significant digits. The digit which would follow that number has been rounded out. If you want to use a number with greater accuracy, the newer version of Visual Basic includes a Decimal type, which is accurate to 28 digits. But some numbers are infinite, and no matter how powerful a computer you use, even if you use a super-computer which can handle numbers up to a couple of hundred places, the last place will still have to be rounded out.

The point is, you have to know what your requirements are, and how many significant digits are needed in order to produce the desired result without incurring a round-off error. If it's greater than the number available with QBasic's data types, then it simply will not produce accurate results. An undeclared variable in QBasic is SINGLE by default. If you have not declared your variables by type, then you can increase the accuracy of your results by declaring your number variable as DOUBLE. Put this statement at the beginning of your program, using the name of your variable for the number. Also see my code for the RoundUP function, which follows.

DIM number AS DOUBLE

--------------------------------------------------

DECLARE FUNCTION RoundUp# (num AS DOUBLE, place AS INTEGER)
DIM num AS DOUBLE, place AS INTEGER, splace AS STRING, E AS STRING
DO
CLS
PRINT "Program will round to nearest digit for specified number ";
PRINT "of decimal places,"
PRINT "or will round to least significant digit of an integer."
PRINT "It will work with either a positive or negative number."
PRINT "Zero places will round to nearest integer."
PRINT : INPUT "Enter number to round: ", num
PRINT : PRINT "Enter number of places to round after the decimal,"
PRINT "negative number of places to left of decimal, or 0 for "
INPUT "rounded integer (place must be between -9 and 9.): ", splace$
place = VAL(splace)
IF place >= -9 AND place <= 9 THEN
PRINT : PRINT "Rounded number is "; RoundUp(num, place)
ELSE
PRINT "Out of range."
END IF
PRINT : INPUT "Enter for another number or Q to quit: ", E$
LOOP UNTIL UCASE$(E$) = "Q"
END