| Fixed-Point Arithmetic | Lesson 8 | Contents |
The basic version of Mops (Mops.dic) utilizes fixed-point arithmetic, also called integer arithmetic instead of floating-point arithmetic. The primary difference between the two is that fixed-point arithmetic functions only with integers. You had a hint of that when you started experimenting with division in Mops: the answer was either an integer quotient or a quotient-plus-remainder (both of which were integers). Floating point arithmetic, on the other hand, allows you to enter numbers with digits to the right of the decimal.
Floating-point arithmetic is convenient in many instances, especially when results of operations traditionally are other than whole numbers: financial calculations, for example, which have cents to the right of the decimal. But floating-point also has some drawbacks, which should be particularly important to you as a Mops programmer
One is that floating-point arithmetic takes up more memory in the computer, increasing the size of the Mops kernel. This is not as significant now as it was a few years ago, when memory was much more expensive.
Second, floating-point arithmetic usually takes more time to calculate than fixed-point. Depending on the computer and the language, a floating-point calculation can take up to ten times as long as the same calculation operating in fixed-point.
And third, floating-point arithmetic can be less accurate than fixed point in some calculations. You cannot, for example, multiply a number by precisely one-third in floating-point arithmetic; you must multiply by 0.33333.... There will always be some error in the calculation, which can compound itself after a couple of further calculations based on this approximation of one-third. If you multiply 9 times 0.3333333, you get 2.9999997, rather than the desired result of 9 times one-third, or 3.
Many programs have no need for floating-point arithmetic at all. For this reason, the basic Mops system has only the smaller and faster fixed-point support, with floating-point available as an option for those who need it.
But fixed-point arithmetic presents a problem of its own, because you may be accustomed to dealing with numbers other than integers, numbers like pi or percentages. To accommodate such numbers, Mops requires that you use scalars, or operations that appear to convert floating-point numbers into fixed-point numbers.
Two of the most used scalars are those that are actually special-case combinations of familiar arithmetic operations:
| */ | ( n1 n2 n3 -- (n1 * n2) / n3 ) | Multiplies n1 times n2 and then divides that result by n3, leaving the final result on the stack |
|---|---|---|
| */MOD | ( n1 n2 n3 -- (n1 * n2) / n3 remainder ) | Same as */ but leaves both the result and the remainder on the stack |
Notice carefully the order of the items on the stack and how they are treated by the arithmetic operations, because they are not as you would expect in a regular combination of Mops arithmetic operations. But the order allows you to better visualize the process by changing the algebraic infix notation of a problem to Mops postfix notation.
To multiply 100 times two-thirds:
100 * 2 / 3 becomes 100 2 3 */
Similar operations can be used to work with percentages. Simply put a 100 in place of the n3 in the description above and the percentage figure in place of n2.
Decimal, Hex, and Binary Arithmetic
When Mops communicates to the Macintosh's built-in routines, it often uses numbering systems other than the traditional decimal (base 10) system. The two most often used non-decimal numbering systems are the hexadecimal and binary. Each has very different characteristics.
The binary system, at the other extreme, has only two digits, a zero and a one. This system may not seem very useful in light of decimal and hexadecimal systems, but as you get further into the Macintosh programming environment, You'll find times when binary math is absolutely essential for ease of designing elements such as cursors, text fonts, and icons. To show you the differences among the three bases, here is a table of the first 20 numbers in each base:
| Decimal | Hex | Binary |
The hexadecimal numbering system is a base-16 system. That is, instead of numbers increasing say, from one to two digits after the "ones" digit has cycled from zero through nine, it cycles after 15 digits. To denote the digits after 9, hexadecimal notation uses the first several letters of the alphabet. Corresponding to decimal 10 is hexadecimal A; decimal 11 is hexadecimal B; and so on through hexadecimal F. Also called "hex" for short, a hexadecimal number is usually preceded by a special sign ($) so you know that $24 is hexadecimal 24 (decimal 36) instead of the decimal 24.
You might have noticed in this list that there is a special relationship between binary and hexadecimal in that each time one place of the hexadecimal number reaches the maximum (F), four places of a binary number reach their maximum (1111). This relationship will prove more important later on. Although the binary numbers shown in the above list are 8 bits wide (each binary digit, that is, a 0 or 1, is called a bit), Mops actually stores numbers on the stack as 32-bit binary numbers. Therefore, even though you type the number 10 (decimal) into the stack, the number is actually stored as: 0000 0000 0000 0000 0000 0000 0000 1010 If you were to calculate how many numbers you could describe within a 32-bit binary number, it would come out to 4,294,967,296, that's over four billion. Plenty big for just about every job you'll put your Mac to. But that's four billion positive numbers. How do you work with negative numbers? |
|---|---|---|---|
| 0 | 0 | 0000 0000 | |
| 1 | 1 | 0000 0001 | |
| 2 | 2 | 0000 0010 | |
| 3 | 3 | 0000 0011 | |
| 4 | 4 | 0000 0100 | |
| 5 | 5 | 0000 0101 | |
| 6 | 6 | 0000 0110 | |
| 7 | 7 | 0000 0111 | |
| 8 | 8 | 0000 1000 | |
| 9 | 9 | 0000 1001 | |
| 10 | A | 0000 1010 | |
| 11 | B | 0000 1011 | |
| 12 | C | 0000 1100 | |
| 13 | D | 0000 1101 | |
| 14 | E | 0000 1110 | |
| 15 | F | 0000 1111 | |
| 16 | 10 | 0001 0000 | |
| 17 | 11 | 0001 0001 | |
| 18 | 12 | 0001 0010 | |
| 19 | 13 | 0001 0011 | |
| 20 | 14 | 0001 0100 |
What distinguishes a signed from an unsigned number is the way you perform operations on them. For example, if you enter a negative number onto the stack, the minus sign shows Mops that you intend to use a signed number. If, on the other hand, you were to enter the number three billion onto the stack, Mops would know that you mean it to be an unsigned number, since anything above the plus-or-minus 2 billion range can only be unsigned.
But you can force the issue if you want, and convert the designation of a number on the stack for use in arithmetic operations and display purposes.
To understand this process, imagine that you are using a tape recorder that has a digital tape counter that counts in binary. If you set the counter to 0000 0000 and start to rewind the tape, the first thing that shows up on the counter is 1111 1111, which is actually -1 with respect to zero. But if you were to fast-forward the tape, the counter's maximum number would also be 1111 1111. That high number would correspond to the 4 billion number of an unsigned number. But as a signed number, 1111 1111 represents the start of counting backwards from zero, that is, -1.
For some hands-on experience with this concept, consider first that the dot command you learned in the early sections of this manual was actually a command to display the signed number equivalent of the number on the stack. That means that it can display numbers only within the plus-or-minus 2 billion range.
Prove it now by entering 3 billion (a three and 9 zeros) on the stack. Sure enough, the stack display will show:
Stack: depth 1 -1294967296
which is a signed number equivalent, a negative number near 1 billion.
Conversely, let's enter a -1 (a signed number) onto the stack. This time, however, you want to display it as an unsigned number. To do this, you use the U. statement, which first converts the number to an unsigned number and then types it to the screen according to the following definition:
| U. | ( n -- ) | Displays the number on the top of the stack as an unsigned, single-precision number |
|---|---|---|
| U< | ( u1 u2 -- boolean ) | Compares two unsigned single-precision numbers. If u1 is less than u2, then leaves TRUE on the stack; otherwise, leaves FALSE |
| U> | ( u1 u2 -- boolean ) | Compares two unsigned single-precision numbers. If u1 is greater than u2, then leaves TRUE on the stack; otherwise, leaves FALSE |
Try this sequence and watch what happens:
-1 U. cr
4294967295
One Last Set of Numbers; ASCII
You had a preview a while back of a set of numbers called ASCII codes. These are numbers that were assigned by an industry standards group to every number, letter, and symbol on the computer keyboard, plus many control codes that computers use to communicate with each other and with peripherals, such as printers. ASCII is an acronym for American Standard Code for Information Interchange. It is this standard that allows computers to communicate so effectively over telephone lines and allows so many different computer terminals to operate with a wide variety of larger computers.
Information from the keyboard reaches the Macintosh as numbers according to this code. The computer recognizes the press of the letter "a" only as the number 97 (decimal). Because each letter and symbol has a unique number, it is possible to make comparisons of a key pressed and manipulate characters on the screen with the many number crunching tools you've already learned. If you know, for example, that all capital letters of the alphabet are numbered from 65 to 90, it is possible to create a DO...LOOP that instantly prints those letters on the screen:
: alphabet 91 65 DO i emit cr LOOP ;
EMIT is a Mops word that displays on the screen the character that is referenced by its ASCII number.
If you want to put a particular ASCII character value on the stack, you can use the Mops word "&".
Try typing
& c
and you'll see that the stack display shows 99, which is the ASCII value for "c". Other Mops words that might go along with EMIT and lsquo;&" are:
| EMIT | ( n -- ) | Displays the character referenced by ASCII number, n |
|---|---|---|
| SPACE | ( -- ) | Displays a blank space on the screen. |
| SPACES | ( n -- ) | Displays n blank spaces on the screen |
Here's a use of SPACES in the alphabet definition to demonstrate its power:
: alphabet 91 65 DO i dup 64 - spaces emit cr LOOP ;
It is also convenient to remember that upper and lower case letters are separated by a factor of 32 regardless of the letter. This may come in handy when you need to convert upper to lower cases or vice versa.
| Previous Chapter | Contents | Next Chapter |
|---|---|---|
| ⇧ | ||
| This page online: http://PowerMops.com/MopsManual/Lessons/Chapter8.html | ||