CHAPTER 9. CHARACTER SEGMENT
9.6
Converting a string to a number
97
We now learned how to parse strings and retrieve components from them. But what if these components are numbers? Well, there is a way in 4tH to convert a string to a number, but like every number-conversion routine it has to act on invalid strings. That is, strings that cannot be converted to a valid number.
4tH uses an internal error-value, called ’(ERROR)’. The constant ’(ERROR)’ is a strange number. You can’t negate it, you can’t subtract any number from it and you can’t print it.
If 4tHs number-conversion word ’NUMBER’ can’t convert a string it returns that constant.
’ERROR?’ checks the return value and leaves an additional true flag if an error occured
(which means: ’(ERROR)’ was returned). Let’s take a look at this program:
." Enter a number: " else
." The number was: " . cr then
\ write prompt refill drop bl parse-word number error?
\ enter string
\ parse string
\ convert to a number
\ test for valid number if \ if not valid
." You didn’t enter a valid number!" drop cr
\ print if valid
You first enter a string, then it is parsed and ’PARSE-WORD’ returns the address and count.
’NUMBER’ tries to convert it. If ’NUMBER’ returns ’(ERROR)’ it wasn’t a valid string.
Otherwise, the number is right on the stack, waiting to be printed. That wasn’t so hard, was it?
9.7
Controlling the radix
If you are a programmer, you know how important this subject is to you. Sometimes, you want to print numbers in octal, binary or hex. 4tH can do that too. Let’s take the previous program and alter it a bit:
." Enter a number: " refill drop bl parse-word number error?
if
\ write prompt
\ enter string
\ parse string
\ convert to a number
\ test for valid number
\ if not valid
." You didn’t enter a valid number!" drop cr else \ print if valid hex
." The number was: " . cr then
We added the word ’HEX’ just before printing the number. Now the number will be printed in hexadecimal. 4tH has a number of words that can change the radix, like ’DECIMAL’ and ’OCTAL’. They work in the same way as ’HEX’.
4tH always starts in decimal. After that you are responsible. Note that all radix control follows the flow of the program. If you call a self-defined word that alters the radix all subsequent conversion is done too in that radix:
CHAPTER 9. CHARACTER SEGMENT 98
: .hex hex . ; \ print a number in hex
." Enter a number: " refill drop bl parse-word number error?
if
." You didn’t enter a valid number!" drop cr else \ print if valid
." The number was: " .hex cr
\ test for valid number
\ if not valid then
\ write prompt
\ enter string
\ parse string
\ convert to a number
In this example not only that single number is printed in hex, but also all subsequent numbers will be printed in hex! A better version of the ".HEX" definition would be:
: .hex hex . decimal ;
Since that one resets the radix back to decimal. Words like ’HEX’ do not only control the output of a number, but the input of numbers is also affected:
." Enter a number: " refill drop bl parse-word hex number error?
if
\ write prompt
\ enter string
\ parse string
\ convert hexadecimal
\ convert to a number
\ test for valid number
\ if not valid
." You didn’t enter a valid number!" drop cr else \ print if valid dup
." The number was: " decimal . ." decimal" cr
." The number was: " hex . ." hex" cr then
’NUMBER’ will now also accept hexadecimal numbers. If the number is not a valid hexadecimal number, it will return ’(ERROR)’. You probably know there is more to radix control than ’OCTAL’, ’HEX’ and ’DECIMAL’. No, we have not forgotten them. In fact, you can choose any radix between 2 and 36. This slightly modified program will only accept binary numbers:
: binary 2 base ! ;
." Enter a number: " refill drop bl parse-word binary number
\ write prompt
\ enter string
\ parse string
\ convert hexadecimal
\ convert to a number error?
if
\ test for valid number
\ if not valid
." You didn’t enter a valid number!" drop cr else \ print if valid dup \ both decimal and hex
." The number was: " decimal . ." decimal" cr
." The number was: " hex . ." hex" cr then
’BASE’ is a predefined variable that enables you to select any radix between 2 and 36. This makes 4tH very flexible. However, this won’t work: hex 02B decimal . cr
CHAPTER 9. CHARACTER SEGMENT 99
4tH will try to compile "02B", but since it isn’t a word or a valid decimal number, it will fail. Words like ’HEX’ and the ’BASE’ variable work only at run-time, not at compiletime! Isn’t there a way to compile non-decimal numbers?
Sure, there is, although it is not that flexible. There are four words that control the interpretation of numbers at compile-time:
1. [BINARY]
2. [OCTAL]
3. [DECIMAL]
4. [HEX]
They work fundamentally different than their run-time equivalents. First, they only work at compile-time. Second, they are interpreted sequentially and do not follow the flow of the program. Let’s take a look at these two programs:
[binary] 101 . cr
[octal] 101 . cr
[decimal] 101 . cr
[hex] 101 . cr
This will print the decimal numbers "5", "65", "101" and "257", since each one of them is compiled with a specific radix.
: binary 2 base ! ; binary 101 . cr octal 101 . cr decimal 101 . cr hex 101 . cr
Now the decimal number "101" is printed in four different radixes, since at compile-time the radix was set to decimal (which is the default). Now take a look at this program:
: do_binary [binary] ;
: do_decimal [decimal] ; do_binary 101 decimal . cr do_decimal 101 decimal . cr
The program will print "101" two times! Haven’t we selected binary at compile-time? No, both ’[BINARY]’ and ’[DECIMAL]’ are interpreted sequentially!
When ’[BINARY]’ is encountered at the first time, it will set the radix at compile-time to binary. When ’[DECIMAL]’ is encountered in the second line, it will set the radix to decimal. When the third line it compiled, the radix is still set to decimal. If you want to make this program work, try this:
[binary]
101 decimal . cr
[decimal]
101 decimal . cr