| First Grade Math for DOS: Adding and Subtracting |
Microsoft has never seen fit to augment the command processor with an ability to add and subtract. Though an extension to the FOR statement to give it the ability to count was included in NT, all other MS operating systems lack even the rudiments of number manipulation. The bits of number handling offered here have only provided the barest of capabilities for the hardcore batch addict. That is about to change.I have seen number lookup routines around for years, but found them wholely unsatisfactory, because of their lumbering speed and shear bulk of code. I had written off DOS as ever having the potential for doing real math. But, the development of three special techniques has changed my mind. Adding and subtracting can be done with 'pure' DOS using only the commands that are included in the command processor (COMMAND.COM).
The approach I have taken is identical to that taught you in the first grade. That is, starting with the 'ones' digit, add the digits together and 'carry' any 'tens' digit forward to the next column; continuing in like manner to the 'tens', 'hundreds', 'thousands' and so on. Further, if you have been exposed to the so called 'new math', you may remember the idea of using a 'number line' in figuring out what the result of adding 6 plus 8 was. For those who don't know what I mean, the idea is to rule off a line with one tick for each digit. Then to add, the ticks for each number are merely counted off the line in sequence. Any first grader can do it.
Here's an example number line with the problem 6 plus 8 measured out on it ...
Notice that the line only goes to 19. This is the maximum length needed to add two digits, plus a possible carry from a previous digits column (9 + 9 + 1 [carry] = 19).
So far, I have described two of the techniques that need to be coded somehow to get DOS adding like a first grader: parsing the number and measuring on a number line. The third is padding (right justifying) the input strings out to the same number of characters. I found that padding simplifies the parsing process.
So, here's the flow of the addition procedure:
Start | Pad the input number string out to eight characters   | Parse a digit from each input number string
---<---
|| | Add the digits | | |
< More digits to add? > Yes
No|
--->---
| Done
| Padding |
Padding a number was a complex task until I stumbled on a very simple algorithm built around the characteristics of the question mark wildcard in a filespec. I previously approached this problem by counting the characters already in the string on the way to figuring out how many padding characters were needed. A breakthrough of sorts occurred when I reversed the process using the following approach ...
Rather than counting the number of characters in the string, the number of padding characters are counted instead (without parsing). Because of the way the command shell interprets the wildcard definitions, those with five, six and seven question marks match the example file named 12345.{. So, three additional characters (zeros) are prefixed to the original file name by the RENaming part of the FOR statement. This insures the name will always be at least eight characters long.
It's amazing to me how a mere change of perspective made a seemingly complex problem so easy to handle.
BTW, this algorithm also led me to an easy solution of another difficult problem; that of comparing numbers (or strings) to determine the relative relationship of the two, as in 'greater than' or 'less than'. That solution is found by following this link.
| Parsing |
The next big hurdle to overcome was that of parsing the numbers to extract the digits one at a time from each number. I have seen parsing techniques, and even present one in these pages (see Extending CHOICE), but in recent months I have been seeking a way that is applicable to all versions of DOS and uses no external commands. I finally developed a approach that does just that, at least for strings up to eight characters long. (There is no length limitation in Win 95.) The approach is limited to characters that are valid for use in naming files, because dummy file names are used.For use as part of a math function, I felt these limitation were inconsequential compared to the gains in speed and portability. So, building on the results of the padding operation above, here are the fundamentals of the approach ...
|
|
Boy! Just five lines of code, but are they ever obscure! Let me try to unpack them for you.
The tricky part of the COPY statement is the peculiar file name of the copy. Except for the leading and trailing pair of percent signs, the trailing question mark and the overall length; the name is fairly arbitrary. The object of the exercise is to create a name that has the first seven letters occupied by a string that could represent an environment variable and the last letter be the last letter of the source file's name. I hope the purpose of the file name will be more evident as this description unfolds.
The next line adds the file name to the end of the output string stored as the temporary batch procedure, {T}.BAT, CALLed in the third line. For the example number, 12345, the statement contained in {T}.BAT would be:
It is assumed the variable named _3456 does not exist and will therfore evaluate as empty when the procedure is called. Voilá, as if by magic, only the last character of the file name (5) is stored into the variable {D}. And never once did my hands leave my arms.
The REName statement, in the next line, truncates the input string to seven characters. This is not really needed except in Win 95, where the next line wouldn't work properly because file names are not automatically truncated to 8.3 format. So it is included for compatability.
Finally, The FOR statement adds a character to the front of the string to restore it to eight characters in length. Prefixing a character to the name requires the original name to be hard coded in the rename statement. Otherwise, the added character replaces the first character in the name, which is not the desired outcome. The FOR statement variable, %%v, supplies the necessary hard coded name.
Of course, to fully parse the string the procedure must loop until all of the characters are processed. The math function under construction here loops after each pair of digits is added, as shown earlier in the flow diagram. So, the looping code is left until later. I have also left out the padding and parsing of the second number. Again, I'll add that later. Right now I'm trying to walk you through the primary algorithims needed to make a math function.
As an aside, I have used the same fundamental approach for a generalized parsing procedure that starts with the left-most character and proceeds to the right. This procedure is found by following this link.
| Measuring on a Number Line |
Okay, now we're finally down to the really good stuff! Lets add two digits together using a number line.For the number line, I chose a recursive command line with number ticks provided as input varibles, thus ...
Pretty simple, really. But, how can you count on it?
Well, using the SHIFT command like this ...
Oh,my! I can hear it now, "Here we go again! Is this guy Lavedas truly demented - or what!"
Okay, I admit it. I don't know how I come up with this stuff. It just rolls around in my head (with the rocks and the few marbles that are left) and every once in a while, something comes together to make some kind of sense and just spews out. (I DO NOT stay up nights thinking about this stuff!)
Anyway, the really important part can be understood if the ninth and tenth lines make sense. The ninth line sets a very special environment variable. One named for the first of the two digits to be added. The tenth line uses the result of the SET to SHIFT the arguments from the command line (i.e. move up the number line). For example, considering the six plus eight problem shown earlier, the ninth line is executed as ...
and with this result, the tenth line evaluates as ...
That's a pretty peculiar looking line of code, but it is actually interpreted by the command shell as meaning ...
Everything after the SHIFT is just ignored. So, the input arguments are shifted to the left (so to speak) moving what was originally the eleventh string on the recursive command line into the position of being the very first one (i.e. %0).
Hey you say, I could have done that with a straightforward loop, shifting the arguments until the %0 one was equal to the input digit safely stored in the {2} variable. Yes, that's true, but that would take several more lines of code and loops are much slower than FOR statements. Besides, a loop cannot be used for performing the shifts equal to the second digit. That would require knowledge of the answer, which we don't know yet.
So, the code for the second digit is nearly identical to that for the first and does the same thing (i.e. shift our number line), but when it has worked its magic, the value returned from the %0 replacable argument is the very one we were after in the first place!
Wait a minute here! There's only one digit! Six plus eight equals 14, not four!
Calm down. I was getting to that. That's what the carry flag variable, [CF], is there for. It's value is determined in the next few lines. Specifically, if the sum of the digits is greater than nine, then there must be nothing left on the command line after the tenth string. That is, the %0 argument is at least '0' and the tenth one (%9) is the final '9' or is empty. To check this, one more shift is needed to bring that eleventh argument into the range of the ten available input variables (%0, %1 ... %9). Then, if it is indeed empty, the result must be greater than nine and if so the carry flag is set to '1'.
Got that? No, well cut and paste the procedure, save it as ADDIGITS.BAT, set the debug variable, DBGA, equal to something and run it. Maybe then it will be clear to you. You may need to put some pauses in it or run it under a secondary command processor with the /Y switch, as in ...
Then maybe it will make sense. I hope so.
Anyway, it's time for the whole enchallada now. Putting the three algorithms together with the padding and parsing of the second number and the looping gives a fully functional adding routine, ADD.BAT.
Drum roll please.
Here it is ------ the full blown adding function:
|
:: ADD.BAT - An efficient 'pure DOS' adding routine.
|
ADD.BAT is much shorter than all other adders I have seen (just 34 lines), but no less difficult to comprehend. I believe it is also much faster than the others out there, though I have not done speed trials. Its speed surprises me, taking a blink of an eye to add four and five digit numbers on my K6 - 200 MHz machine at home. Heck, it only takes a second or two on my ancient 486Dx2 at work. I think it's almost fast enough to be considered a tool, rather than just a toy. But you can be the judge. Try it yourself and let me know what you think.Oh, BTW, subtraction only required a tiny bit more code and a few minor modifications once the ADD.BAT code was debugged. Check it out here.
| Related Topics |
I have mentioned three related subjects, parsing for Windows 95, determining greater/less than relationships and subtraction. In addition, there is adding to and subtracting one from a digit (i.e. incrementing and decrementing a number). For your convienience, these links are also provided below:
Parse 95
Comparisons
Subtraction
Incrementing/Decrementing