Next: , Previous: Simple numeric output, Up: Other I/O


5.19.2 Formatted numeric output

Forth traditionally uses a technique called pictured numeric output for formatted printing of integers. In this technique, digits are extracted from the number (using the current output radix defined by base), converted to ASCII codes and appended to a string that is built in a scratch-pad area of memory (see Implementation-defined options). Arbitrary characters can be appended to the string during the extraction process. The completed string is specified by an address and length and can be manipulated (TYPEed, copied, modified) under program control.

All of the integer output words described in the previous section (see Simple numeric output) are implemented in Gforth using pictured numeric output.

Three important things to remember about pictured numeric output:

<#              core       “less-number-sign”

Initialise/clear the pictured numeric output string.

<<#              gforth       “less-less-number-sign”

Start a hold area that ends with #>>. Can be nested in each other and in <#. Note: if you do not match up the <<#s with #>>s, you will eventually run out of hold area; you can reset the hold area to empty with <#.

#       ud1 – ud2         core       “number-sign”

Used within <# and #>. Add the next least-significant digit to the pictured numeric output string. This is achieved by dividing ud1 by the number in base to leave quotient ud2 and remainder n; n is converted to the appropriate display code (eg ASCII code) and appended to the string. If the number has been fully converted, ud1 will be 0 and # will append a “0” to the string.

#s       ud – 0 0         core       “number-sign-s”

Used within <# and #>. Convert all remaining digits using the same algorithm as for #. #s will convert at least one digit. Therefore, if ud is 0, #s will append a “0” to the pictured numeric output string.

hold       char –         core       “hold”

Used within <# and #>. Append the character char to the pictured numeric output string.

sign       n –         core       “sign”

Used within <# and #>. If n (a single number) is negative, append the display code for a minus sign to the pictured numeric output string. Since the string is built up “backwards” this is usually used immediately prior to #>, as shown in the examples below.

#>       xd – addr u         core       “number-sign-greater”

Complete the pictured numeric output string by discarding xd and returning addr u; the address and length of the formatted string. A Standard program may modify characters within the string.

#>>              gforth       “number-sign-greater-greater”

Release the hold area started with <<#.

represent       r c-addr u – n f1 f2        float       “represent”

f>str-rdp       rf +nr +nd +np – c-addr nr         gforth       “f>str-rdp”

Convert rf into a string at c-addr nr. The conversion rules and the meanings of nr +nd np are the same as for f.rdp. The result in in the pictured numeric output buffer and will be destroyed by anything destroying that buffer.

f>buf-rdp       rf c-addr +nr +nd +np –         gforth       “f>buf-rdp”

Convert rf into a string at c-addr nr. The conversion rules and the meanings of nr nd np are the same as for f.rdp.

Here are some examples of using pictured numeric output:

     : my-u. ( u -- )
       \ Simplest use of pns.. behaves like Standard u.
       0              \ convert to unsigned double
       <<#            \ start conversion
       #s             \ convert all digits
       #>             \ complete conversion
       TYPE SPACE     \ display, with trailing space
       #>> ;          \ release hold area
     
     : cents-only ( u -- )
       0              \ convert to unsigned double
       <<#            \ start conversion
       # #            \ convert two least-significant digits
       #>             \ complete conversion, discard other digits
       TYPE SPACE     \ display, with trailing space
       #>> ;          \ release hold area
     
     : dollars-and-cents ( u -- )
       0              \ convert to unsigned double
       <<#            \ start conversion
       # #            \ convert two least-significant digits
       [char] . hold  \ insert decimal point
       #s             \ convert remaining digits
       [char] $ hold  \ append currency symbol
       #>             \ complete conversion
       TYPE SPACE     \ display, with trailing space
       #>> ;          \ release hold area
     
     : my-. ( n -- )
       \ handling negatives.. behaves like Standard .
       s>d            \ convert to signed double
       swap over dabs \ leave sign byte followed by unsigned double
       <<#            \ start conversion
       #s             \ convert all digits
       rot sign       \ get at sign byte, append "-" if needed
       #>             \ complete conversion
       TYPE SPACE     \ display, with trailing space
       #>> ;          \ release hold area
     
     : account. ( n -- )
       \ accountants don't like minus signs, they use parentheses
       \ for negative numbers
       s>d            \ convert to signed double
       swap over dabs \ leave sign byte followed by unsigned double
       <<#            \ start conversion
       2 pick         \ get copy of sign byte
       0< IF [char] ) hold THEN \ right-most character of output
       #s             \ convert all digits
       rot            \ get at sign byte
       0< IF [char] ( hold THEN
       #>             \ complete conversion
       TYPE SPACE     \ display, with trailing space
       #>> ;          \ release hold area
     

Here are some examples of using these words:

     1 my-u. 1
     hex -1 my-u. decimal FFFFFFFF
     1 cents-only 01
     1234 cents-only 34
     2 dollars-and-cents $0.02
     1234 dollars-and-cents $12.34
     123 my-. 123
     -123 my. -123
     123 account. 123
     -456 account. (456)