FORTH and the numbers
published: 14 May 2019 / updated 24 June 2020
Stacking and unstacking integers numbers
The FORTH language stores numbers on a stack called data stack or parametric stack. Stacking a number is very simple:
55
stacks the number 55. There are several methods to unstack this number. The simplest
is to display the contents of the last stacked item. The word .
(dot) displays 55.
55 ok . 55 ok
If you put multiple numbers on the data stack, this is what happens:
11 45 6543
The numbers are put on the data stack in typing order. The last number entered is always at the top of the stack:
stacked value | top -- stack -- bottom |
---|---|
11 | 11 |
45 | 45 11 |
6543 | 6543 45 11 |
The successive unstacking of the numbers displays these in the reverse order of their stacking:
11 45 6543 ok . 6543 ok . 45 ok . 11 ok
To better visualize the mechanism of stacking and unstacking numbers, think of a stack of plates: the last plate placed on the stack will be the first recovery.
At any time you can read the contents of the data stack without having
to cause the values stored there to be unstacked using the word .S
:
1 2 3 .S
display 1 2 3
This storage principle is also called LIFO stack (Last In, First Out) in some works written in English to designate a stack whose mechanism is: "last in, first out".
With the majority of FORTH language versions, the amount of stackable numbers is quite high, but remains limited. If you stack too many numbers, you will overload the data stack. Likewise, any attempt to unstack a number while the data stack is empty, will display an error message.
Each stacked number can only be an integer in simple precision format. Depending on the case, this number can be considered signed or unsigned:
- signed or unsigned: arithmetic calculation parameter
- unsigned: simple precision address
In signed single precision format, the most significant bit indicates the sign of the number.
Specific gForth
The gForth version of the FORTH language uses a double precision data stack. So, all the manipulations explained here mentioning double precision numbers these numbers are 64-bit values with gForth.
The value -1, deposited on the stack as an integer value has the following binary representation:
1111111111111111
(simple precision).
This same value can be displayed in absolute value using the word U.
instead of the word .
:
-1 U. \ display 65535 (simple precision) \ display 4294967295 (gForth)
Depending on whether we consider single precision integers signed or unsigned, their definition interval is:
- simple precision signed between [-32768..32767]
- simple precision unsigned between [0..65535]
The use of any number outside this range will have no meaning in a single precision stack environment:
100000 . \ or -100000 U. \ will display a value with no apparent connection to 100000.
This residual value corresponds to the least significant part on simple precision of the number 100,000 and did not overflow when stacked on the data stack.
This has no practical use and can be compared to the hosting attempt a combine harvester in an enclosure reserved for bicycles, with for you less risk of not being sued by bicycle owners damaged by this more than doubtful move. This limitation may seem restrictive at first, but FORTH's performance comes at this price.
Processing whole numbers is much faster than processing numbers expressed in different formats. You will find, in use, that the numbers integers are sufficient in most cases. We will see later, that FORTH is not limited to the treatment of whole numbers simple precision: it suffices to express complex data on more than one cell in the stack.
If you are a complete beginner in FORTH, take the time necessary to properly understand what the data stack is, its mechanism and its evolution: the concept of pile is certainly the most important of the FORTH language.
Elementary arithmetic operations
The arithmetic operators + - *
and /
act on both
values at the top of the data stack. The processed values are always
signed single precision integers.
Sum of two integers numbers
To add two numbers, first put them on the data stack:
22 44 + .
display 66
Once stacked 22 and 44, the word +
adds these two
values and the word .
displays the result:
55 1 + 3 + . \ display 59 and can also be written 55 1 3 + + .
The addition is commutative: the values can be deposited on the stack of data in any order:
55 22 + 22 55 +
This calculation principle is called REVERSE POLISH NOTATION. We can also add two numbers
unsigned integers, provided you display the result using the
word U.
instead of .
:
35000 10 + U.
It is also possible to add two different sign numbers:
10 -5 + . \ display 5 -5 10 + . \ also display 5
Depending on whether we treat values considered to be signed or not, the result definition intervals must be respectively in [- 32768..32767] or [0..65535]. Any result outside of these intervals would make no sense.
FORTH also has the word 1+
which increments the value located
at the top of the 1 unit data stack:
10 1+ \ is equivalent to 10 1 +
Subtract two integers numbers
Let two numbers a and b. The difference of two numbers will be written in FORTH in the form:
a b -
for a-b
Subtraction is not commutative:
10 3 - . \ display 7 3 10 - . \ display -7
The word 1-
decrements the value at the top of the stack
data of 1 unit:
10 1- \ is equivalent to 10 1 -
Product of two integer numbers
Let two numbers a and b. The product of two numbers will be written in FORTH in the form:
a b *
for a*b
La multiplication est commutative:
7 5 * . \ or 5 7 * . \ display 35
The word 2*
multiplies the value at the top of the data stack by two:
5 2* \ is equivalent to 5 2 *
Quotient of two integer numbers
For the division, only the whole quotient is kept on the data stack:
22 7 / . \ display 3
The division is not commutative:
15 5 / . \ display 3 5 15 / . \ display 0
The rest of the division can be obtained by applying the modulo function:
22 7 MOD . \ display 1
The modulo function can be used to determine the divisibility of one number by another:
: DIV? ( n1 n2 ---) OVER OVER MOD CR IF SWAP . ." is not " ELSE SWAP . ." is " THEN ." divisible by " . ;
The word /MOD
combines the actions of /
and MOD
:
22 7 /MOD . . \ display 3 1
Product and quotient of three numbers
valid only for single precision stack
If we try an operation of the following type:
30000 3 * 10 / .
we may be somewhat surprised by the result. But everything can be explained,
because the product calculated first delivers a value whose capacity is
higher than that accepted by the signed single precision values. To treat
these special cases, we will preferably use the word */
which combines multiplication and division operations, but deals with
the transient result of the multiplication in double precision format.
Example, either to calculate the price including tax of a good (VAT at 20%), we will define the word ATI as follows:
: ATI ( n1 --- n2) DUP 200 1000 */ + ; 442 ATI . \ display 530
The processed values are expressed in cents.
The word */MOD
has the same properties as */
, but delivers
the quotient and the rest of the operation.
For example, to give immediate and practical application of the concepts already expressed, is the conversion of degrees Fahrenheit and Celsius:
- the conversion of degrees Fahrenheit to degrees Celsius follows the formula
°C=(°F-32)*5/9 - converting from degrees Celsius to degrees Fahrenheit follows the formula
°F=9/5*°C+32
: C>F ( °C --- °F) 9 5 */ 32 + ; : F>C ( °F --- °C) 32 - 5 9 */ ; 37 C>F . \ display 98 (results are rounded)
This example works on all versions of the FORTH language.
Processing of algebraic expressions
Operations can be chained, but an operation in algebraic notation with parentheses must be converted to RPN notation taking into account the order of priority of operations. FORTH does not use parentheses in arithmetic operations:
let's see the algebraic expression ( 2 + 5 ) * ( 7 - 2 )
it is written in FORTH2 5 + 7 2 - *
During an algebraic notation conversion operation infixed to notation reverse polish, always start with the highest parenthesis level nested and from the left. Write the transcription in Polish notation reverse of each operation on separate lines, successively from top to bottom, putting them in the extension of the expression algebraic expressed in the initial formula:
By taking each level in order, we rewrite the formula:<
 2 5 + 7 2 - * 5 2 + 3 * /
306/5000 It's shocking? But all the interpreters / compilers work like this when they have to evaluate an algebraic formula. In algebraic notation, parentheses only serve to isolate an expression as a subexpression which becomes a member of a more general expression.
In IT as in arithmetic, an operator is always working on two operands and only two operands simultaneously. The result of an operation relating to two operands delivers a value which can in turn become the operand of another operator. The order of operands and operators is fundamental:
algebraic notation | reverse polish |
---|---|
(2+3)*5 |
2 3 + 5 * |
2+(3*5) |
2 3 5 * + or
3 5 * 2 + |
All arithmetic problems can be solved in this way, is just a matter of habit. The example given above illustrates perfectly the rigor which the Forth programmers must demonstrate. This rigor guarantees unambiguous operation of the programs, whatever their level of complexity.
The postfixed notation (RPN) used by the FORTH language can quickly become a real headache when dealing with somewhat complex arithmetic expressions:
Handling data on the stack
The data stack is the fundamental element of the FORTH language for processing of data. Its operation is identical to that of the stack managed by the microprocessor. In certain situations, the data processed by different definitions must be reordered or duplicated.
The word DUP
duplicates the contents of the top of the data stack:
10 DUP . . \ display 10 10 5 DUP * . \ display 25
The word OVER
duplicates the second element of the data stack:
5 15 OVER . . . \ display 5 15 5
The word SWAP
reverses the two elements at the top of the data stack:
1 3 SWAP . . \ display 1 3
The word ROT
rotates on the three elements at the top of the data stack:
1 2 3 ROT . . . \ display 1 3 2
The word -ROT
performs a reverse rotation on three elements. His
behavior is similar to executing two successive ROT
.
The word PICK
places the nth element on the stack at the top of the data stack
data, n not included. The starting point for counting the items to be processed
is 0 and not 1, element number zero being located immediately after the parameter
processed by PICK
. The sequence 0 PICK
is similar to DUP
,
1 PICK
to OVER
. There is no error handling if n is
greater than the number of items dropped on the data stack. Example:
1 2 3 4 5 6 4 PICK \ stacks 2 because 6 is element n ° 0, 5 is element n ° 1, etc ...
The word ROLL
rotates on the first n elements
of the data stack, n not included. As for PICK
, the base
starting point for counting the elements to be treated is 0:
1 2 3 4 5 6 4 ROLL . . . . . . \ display 2 6 5 4 3 1
Here are some examples of how to use these data stack manipulators:
: SQUARED ( n --- n2)
DUP * ;
The word SQUARED
raises any integer squared:
2 SQUARED . display 4
3 SQUARED . display 9
4 SQUARED . display 16
: TO-CUBE ( n --- n3)
DUP DUP * * ;
Le mot AU-CUBE élève un nombre entier quelconque au cube:
2 TO-CUBE . display 8 3 TO-CUBE . display 27
332/5000 Attention, do not use too high values, because a result higher than 32767, for a 16-bit data stack, becomes false. Always keep in mind that data processed are simple precision integers, therefore of limited capacity. We will see later how to deal with larger numbers.
Passing parameters through the return stack
Next to the parameter stack, there is a second stack in FORTH, called return stack because it is used by the internal interpreter to find the address back on each call to a procedure.
There are sometimes extreme cases where we may have to store one or more
parameters elsewhere than on the data stack, this to simplify some
few scabrous manipulations. the most convenient solution, among others,
is the return stack. This stack is accessible by the words >R
and R>
with some precautions so as not to compromise operation
of this internal stack.
The word >R
transfers an integer from the data stack to
the return stack.
The word R>
transfers an integer from the return stack to the data stack.
An >R R>
operation is null. At the end of the definition, there must be
as many >R
as R>
under penalty of disturbing some
little the normal course of your definition. Example of use:
: SQUARED ( n --- n^2) \ squared elevation DUP >R \ transfer of copy to return stack CR ." Le carré de " . \ initial value display ." est " R> DUP * . ; \ recovery value deposited on return stack \ and display of its square////
Control of the display of integer numbers
In FORTH, the single precision integers deposited on the data stack can be displayed
by executing the word .
(dot). But other words allow to execute
a more presentable display.
The word .R
displays a signed single precision number framed on the right in a field of n characters. Example:
3 10 .R
display 3 (3 preceded by 9 spaces)
101 10 .R
display 101 (101 preceded by 7 spaces)
And here is a very practical application:
: C>F ( °C --- °F) \ Convert Celsius to Fahrenheit 9 5 */ 32 + ; : .HARDENING ( °C ---) \ °C and °F formatted displays DUP 6 .R ." °C " C>F 6 .R ." °F" ; : STEEL-HARDENING ( ---) \ Tempering temperature table CR ." STEEL HARDENING COLORS:" CR CR ." dark red............ " 680 .HARDENING CR ." dark cherry red..... " 740 .HARDENING CR ." cherry red.......... " 770 .HARDENING CR ." light cherry red.... " 800 .HARDENING CR ." light red........... " 850 .HARDENING CR ." very light red...... " 900 .HARDENING CR ." red yellow.......... " 950 .HARDENING CR ." yellow.............. " 1000 .HARDENING CR ." yellow light........ " 1100 .HARDENING CR ." yellow white........ " 1200 .HARDENING CR ." white............... " 1300 .HARDENING CR CR ;
We execute STEEL-HARDENING
:
steel-hardening STEEL HARDENING COLORS: dark red............ " 680°C 1256°F dark cherry red..... " 740°C 1364°F cherry red.......... " 770°C 1418°F light cherry red.... " 800°C 1472°F light red........... " 850°C 1562°F very light red...... " 900°C 1652°F red yellow.......... " 950°C 1742°F yellow.............. " 1000°C 1832°F yellow light........ " 1100°C 2012°F yellow white........ " 1200°C 2192°F white............... " 1300°C 2372°F
The word U.R
proceeds in the same way as .R
, but
displays the unsigned whole number:
1 10 U.R \ display 1 -1 10 U.R \ display 65535
Example, either to display a sequence of addresses in hexadecimal and the equivalent in decimal in parentheses:
: .H(D) ( n ---) BASE @ >R \ current digital database backup DUP HEX 4 U.R \ display in hexadecimal in a 4 char field. SPACE 40 EMIT \ display of a space and open parenthesis DECIMAL 5 U.R \ display in decimal in a 5 character field ." )" \ display of closed parenthesis R> BASE ! ; \ current digital base restoration DECIMAL CR 256 .H(D) \ display 100 ( 256) CR 65535 .H(D) \ display FFFF (65535) HEX CR FF .H(D) \ display FF ( 255) CR FFFF .H(D) \ display FFFF (65535)
Double precision integers
Integers with gForth
With the gForth version of the FORTH language, single integers are 32 bits.
With gForth, the following explanations mentioning double precision numbers relate to 64-bit values.
For versions on Arduino, double precision numbers are 32-bit.
FORTH does not natively know floating point numbers. However, routines can be compiled to perform calculations on this type of numbers, but it will be at the expense of the processing speed. If you want to process large quantities, such as your bank or the price of your next car you can use the double numbers precision also called double precision numbers. These are distinguished from whole numbers simple precision by introducing a point:
135246. D. \ display 135246
The point can be placed elsewhere than at the end of the number:
135.246 D. \ display 135246
The elementary operations executable on double precision numbers are addition and subtraction:
3. 5. D+ \ similar as 3 + 5 3. 5. D- \ similar as 3 - 5
Double precision numbers at the top of the stack can be displayed by the following words:
D.
pronounce 'D-dot'. Displays a signed double precision number.UD.
pronounce 'U-D-dot'. Displays an unsigned double precision number.D.R
pronounce 'D-dot-R'. Displays a double precision signed number framed on the right in a field of n characters.UD.R
pronounce 'U-D-dot-R'. Displays an unsigned double precision number framed to the right in a field of n characters.
Exemples:
5. D. \ display 5 -1. UD. \ display 4294967295 (18446744073709551615 in gForth) 10. 12 D.R \ display 10 35.3 12 UD.R \ display 353
A signed single precision integer can be transformed into a signed double precision number
by running the word S>D
. Examples:
10 S>D D. \ display 10 -5 S>D D. \ display -5
To transform an unsigned single precision whole number into a double number unsigned precision, simply stack the simple precision value 0 on the stack after stacking the unsigned simple precision integer. Example:
10 0 D. \ display 10 -5 0 D. \ display 65531 The reverse operation is possible if the \ double precision value is between 0 and 65535: 10. DROP . \ display 10
When a double precision number is deposited on the data stack, the DPL
variable
keeps the position of the decimal point; if it's a single precision number which is
deposited on the data stack, the DPL
variable contains the value -1.
We can use this value to define a word ensuring a conditional conversion:
: ?S>D ( d ou n --- d) DPL @ 0< IF S>D THEN ; 10 ?S>D D. \ display 10 10. ?S>D D. \ display 10
It is interesting to compare the values of DPL
for different positions
from the decimal point when processing a double precision number:
3314. DPL @ . \ display 0 331.4 DPL @ . \ display 1 33.14 DPL @ . \ display 2 3.1.4 DPL @ . \ display 1
Only the position of the second point is kept in DPL
.
Double precision multiplication and division operations are not defined,
with the exception of the words D2*
0 and D2/
:
10. D2* D. \ display 20 10. D2/ D. \ display 5
But with FORTH primitives, there is no problem in defining words to multiply a double precision number by three, four, five or more if applicable:
: D3* ( d --- d*3) 2DUP 2DUP D+ D+ ; : D4* ( d --- d*3) D2* D2* ; : D5* ( d --- d*5) 2DUP D2* 2SWAP D3* D+ ; \ etc...
Simple / double precision mixed operations
Double precision numbers are generally too large to be interesting to apply a treatment of the product or quotient type to them. However, some operators mixed arithmetic allow these operations to be performed.
The word MU/MOD
divides an unsigned double precision number by a
simple precision number signed; leave the double precision quotient on the stack
unsigned and the rest signed. Example:
11. 2 MU/MOD D. . \ display 5 1
The word M/MOD
divides a double precision number signed by a number
simple precision signed; leave the quotient on the stack and the rest in format
simple precision signed. Example:
-12. 2 M/MOD . . \ display -6 0
The word U*D
multiplies two unsigned single precision numbers and
leaves the result in unsigned double precision format on the stack. Example:
10 30 U*D D. \ display 300
The word *D
multiplies two signed single precision numbers and leaves
on the stack the result in double precision format signed. Example:
-10 30 *D D. \ display -300