Input Range Testing, Tolerances
This chapter will start us off by learning how to check data that was input by an operator using an INPUT statement. Let's assume that you have a program that requires the user to input a positive number, and that zero can be allowed. What happens if the user enters in a negative number? How do you check it, and how can you correct it? Let's find out! Here is a section of program code that has our input checking property:
... GETNUM: PRINT "Enter a positive number"; INPUT A IF A >= 0 THEN GOTO NUMOK PRINT "You have entered a negative number! Try again" GOTO GETNUM NUMOK: LET X=2*(A/D) ...
Let's trace through this program section to see what it does. When the program gets to the second line, it prints out the message indicating what the user needs to do. Line 3 then prints a question mark on the screen and waits for the user to type in a number. When the user enters the number, it gets assigned to the variable A, and the program continues to line 4. Here we have one of those wonderful IF...THEN commands. What it does is check the value of A to see if it is greater than or equal to (remember those from chapter 2?) zero. If it is, we have a valid response and the program jumps to line 7. If not, the program drops down to line 5 and prints out the "now look what you did" message. Then line 6 tells us to go to line 1, where the user is again asked to enter the positive number, and we do it all over again.
Now we get to write a "really neat" program. It actually calculates square roots. The method that is used is called "divide and average", and it works like this. Take the number you want to find the square root, and divide it by some arbitrary number. In our case, we'll divide it by 2. Now we take that answer, and divide the original number by it. Now we average the first answer with the new answer, and divide the original number by that result. We now take what we get and average it with the answer we got before. We continue this crazy cycle until the two answers are identical, in which case we will have the square root. Let's run through it using a real number so that you can understand what on earth is going on around here. We'll use 16 since it's a perfect square.
Take the 16 and divide it by 2. You get 8. Now take the 16 and divide it by the 8, and you get 2. Now average the 2 and the 8 to get 5. Now we divide the 16 by 5 and get 3.2. We average our new answer of 3.2 with our old answer of 5 to get 4.1. Now we divide 16 by 4.1 to get (approximately) 3.9. Now we average the 3.9 and the 4.1 to get 4. Now divide 16 by 4 to get 4. Since the new answer and the previous average are the same, we have arrived at the square root. Let's put that into QBASIC. We will start out by asking for a number, so our first lines will be PRINT and INPUT commands to get a number. Be sure to clear out any program that is in memory:
PRINT "Enter Number to Find Square Root"; INPUT NUM
You will notice that we have to keep track of two answers. We will refer to them as new answer and old answer. We'll use the somewhat descriptive variable names NEWANS and OLDANS to keep track of them:
LET OLDANS = NUM / 2Next, we divided the original number by the old answer to get a new answer. We will also need a label here for our loop. We'll use an abbreviation of "Calculate Answer" without spaces (you can't have spaces in label names).
CALCANS: LET NEWANS = NUM / OLDANS
Next, we average the two answers together.
LET OLDANS = (NEWANS + OLDANS) / 2
After that, we go back and do it again.
Now, we need to check when the answers are the same. We'll do that right after we do the division, so insert this line right after the LET OLDANS= line:
IF NEWANS = OLDANS THEN GOTO FINISHED
When we finally get the answer, all we have to do is print it out and end the program. Add this to the bottom of the program:
FINISHED: PRINT "Square Root Of "; NUM; " Is"; NEWANS END
Just to make sure you have it in right, here's what we have so far:
PRINT "Enter Number to Find Square Root"; INPUT NUM LET OLDANS = NUM / 2 CALCANS: LET NEWANS = NUM / OLDANS LET OLDANS = (NEWANS + OLDANS) / 2 IF NEWANS = OLDANS THEN GOTO FINISHED GOTO CALCANS FINISHED: PRINT "Square Root Of "; NUM; " Is"; NEWANS END
Now go ahead and run the program, using the value of 16. Notice that almost immediately it calculates the square root? Let's do it again! Run the program and type in 1234321 when it asks. Right away, you get back the correct answer of 1111. For those of you who have the slower PC's and XT's, you may detect about a tenth of a second delay between pressing the Enter key and the computer coming back with the answer. That is because the computer is going through that loop 13 times, doing all those calculations. If you have a 200 Mhz Pentium system, you almost get the answer before you ask the question because it's so fast! Anyway, let's try another number! Run the program again, and type in 24. Hey! Who told the computer to take a coffee break? When do I get my answer? Well, you will never get your answer! To stop the computer, hold down the Ctrl key while you press the Break key. This is called the "Control-Break" sequence. You probably never would have guessed that in a kazillion years. Here is where we need to talk just a little bit about how a computer sees numbers (and letters, for that matter). In our numbering system, we have ten digits, zero through nine. Well, a computer runs on electricity, and the electricity can only be on or off. Therefore, a computer can only use two digits, zero and one. For the most part, the conversion between these two numbering systems is pretty much accurate and hassle free. For example, what we see as 47, the computer sees as 00101111. When you tell the computer 113, it is really thinking 01110001. However, when you tell the computer 0.3, it has to think 0.0100110011001100110011001100110011001100110011001100110011001... and has a headache, because that is a never-ending sequence. So, the computer has to chop it off at about 38 digits to keep from using too much memory, and yet still contain some accuracy. When it goes back and forth between the two numbering systems, it will sometimes generate this "rounding-off error", causing two things that are supposed to be exactly equal look like they aren't. This is the problem that our little program is having with the number 24. Well then, how do we fix that? Instead of checking if the two answers are equal, we will check to see if they are very, very, very close. How do we do that? Well, if two numbers are very close, when you subtract one from the other, you will get an answer that is very near zero. Let's fix up our program to take this "tolerance" into account. Replace the line that says
IF NEWANS = OLDANS THEN GOTO FINISHED
IF ABS(NEWANS - OLDANS) < .00001 THEN GOTO FINISHED
Now if you run the program, it will work fine. Notice that we took the absolute value of the difference? That way, we would get a positive result no matter which way the numbers are. If the difference of these two numbers is less than 0.00001, a really really really small number, then we will assume that they are the same. However, there is a minor flaw with this method. As we find the square roots of larger numbers, the tolerance become apparently much smaller, because the answers are bigger. For huge numbers, say 30 digit numbers, we may still get the computer to lock up. We could use a slightly bigger number on the right side of the less-than sign, but that would reduce the accuracy of the result for the smaller answers. So how do we fix that? We can make the tolerance change by not using an actual number, but instead by using a percentage of the original number. This will make the tolerance very tight for small numbers, and loosen it for huge numbers. To do that, we simply multiply our original number by a small constant. The corrected line will look like this:
IF ABS(NEWANS - OLDANS) < .00001 * NEWANS THEN GOTO FINISHED
Now the answer will be within .001% of the original number. That's pretty accurate, isn't it? There is still a major problem with the program. Run it again, and try to find the square root of -16. Oh, look! Another coffee break! Why? Each time it finds a new answer, the sign changes! First positive, then negative, then positive again, and so on. It will never zero in on a number but jump around wildly as it tries to average a positive and negative number. If the two numbers are close, but one is positive and the other negative, it will average out to be close to zero. When you divide a number by another very small number, you get a very large answer, and then it will average the new large answer with the old small number. How do we stop this? We don't let the user enter a negative number. How? We showed you at the beginning of the chapter. See if you can figure it out first, but first notice that entering a zero will also give you a "Division by zero" error - we don't want that, either.
Here is the program segment you need to enter to do the job. Between the
LET OLDANS = NUM / 2
IF NUM > 0 THEN GOTO ENTEREDOK BEEP PRINT"Number must be greater that zero - Try again" GOTO TOP ENTEREDOK:
and at the top, add:
Whoa! Look at the line that says "BEEP"! what does that do? What do you think it might do? Would you like to find out? Then type in BEEP in the immediate window and listen closely! To be completely technical, it produces an 800 Hz tone for 0.27 seconds. In real English, it makes the computer beep. I guess in this case you could call it an error alarm! It alerts the user that he has made an input error, and is accompanied by the "nice going, stoopid!" message. The user is then allowed to try again.
Just to make sure you have everything typed in correctly, here is the program in it's entirety:
TOP: PRINT "Enter Number to Find Square Root"; INPUT NUM IF NUM > 0 THEN GOTO ENTEREDOK BEEP PRINT "Number must be greater that zero - Try again" GOTO TOP ENTEREDOK: LET OLDANS = NUM / 2 CALCANS: LET NEWANS = NUM / OLDANS LET OLDANS = (NEWANS + OLDANS) / 2 IF ABS(NEWANS - OLDANS) < .00001 THEN GOTO FINISHED GOTO CALCANS FINISHED: PRINT "Square Root Of "; NUM; " Is"; NEWANS END
If you want to see the intermediate answers, add the program line that's in
TOP: PRINT "Enter Number to Find Square Root"; INPUT NUM IF NUM > 0 THEN GOTO ENTEREDOK BEEP PRINT "Number must be greater that zero - Try again" GOTO TOP ENTEREDOK: LET OLDANS = NUM / 2 CALCANS: LET NEWANS = NUM / OLDANS PRINT "Intermediate Answer is"; NEWANS LET OLDANS = (NEWANS + OLDANS) / 2 IF ABS(NEWANS - OLDANS) < .00001 THEN GOTO FINISHED GOTO CALCANS FINISHED: PRINT "Square Root Of "; NUM; " Is"; NEWANS END
That's all for this time, but here are some programs for you to try your hand at:
INPUT a three digit number, and print it's reversal. In other words, given 275, print 572. You will need input range checking for this program to work. An original number ending with zero (420) can be printed out without leading zeros (24 is okay, since the computer won't print 024). If you would prefer, you may print it out as a three-digit number (024), but the PRINT command will automatically put spaces between each number (that's okay). HINT: Use division and either the INT(x) or FIX(x) functions.
INPUT a four digit number and print its reversal. In other words, given 9371, print 1739. Use the same hint as above.
Given a positive number less that 20,000, (Use INPUT and error checking)
determine if it is a prime number. A prime number can be divided evenly only by
1 and itself. A word of caution here. On the lower speed computer, the larger
numbers (over 10,000) can take over a minute to calculate.
Next chapter, we will learn about random number generation, and talk about a major problem: storing large amounts of data in variables. See you next time!!
Introduced In This Chapter:
Concepts: Input range checking, tolerance, audible