BCD data type: formatting

You can format BCD numbers two ways: using templates or using formatting commands.

Template formatting

You can use a template to format a BCD number:

  output "<$,NNZ.ZZ>" % total-price

This template yields the results shown for each of the following values (notice the rounding of the last three examples):

  1            $1.00
  23.95        $23.95
  12345678.99  $12,345,678.99
  345.567      $345.57
  9.999999999  $10.00
  5456.652     $5,456.65

Here's how the template works:

The "<" and ">" characters delimit the template. This is how OmniMark can tell a template from a format command.

The "$" is the literal character "$". You can use any of the following as literal characters in a template:

  • "$", "#", and "%" (escaped as "%%")
  • any ASCII (or EBCDIC) character between 128 and 255
  • any UTF-8 encoded character over 255
This allows you to insert most currency symbols and other symbols you might want to use for formatting a number as a string. Thus to format a number representing British pounds, you could write:
  output "<%163#,NNZ.ZZ>" % total-price

This template yields the results shown for each of the following values:

  1            1.00
  23.95        23.95
  12345678.99  12,345,678.99
  345.567      345.57
  9.999999999  10.00
  5456.652     5,456.65

If you need to insert literal characters not covered by the list above, you can do so by enclosing them in "<" and ">".

The "," is used to indicate grouping. It allows us to format 12345678.99 as $12,345,678.99, instead of $12345678.99. You can include as many groups as you like, with a format like <N,NN,NNN,NN,N.NN>. If you place the comma before the first group of placeholders like this, <,NNN.ZZ>, it repeats the leftmost group often enough to accommodate the entire number. Thus <,NNN.ZZ> can format an arbitrarily large number, with digits grouped in threes and separated by commas.

The "," grouping character works with the "." decimal character. In the format <$,NNZ.ZZ>, the "," and the "." delimit a group of three. This grouping is then repeatable as many times as required. You can also use "_" and spaces as grouping characters.

The "N" and "Z" characters are placeholders. They represent the positions in the template that will be occupied by the digits that make up the number. If there is a digit in that position, the digit is shown. Different types of placeholders are used to indicate what should be done when the number does not supply a digit in the position of the placeholder.

For instance, if we format the number 23 with the template <$,NNZ.ZZ>, only two placeholders are matched by a digit in the number being formatted—the "N" and the "Z" immediately before the decimal point. That leaves the first "N" and the last two "Z" placeholders unmatched.

In applying the format, unmatched "N" placeholders are replaced by nothing. They are simply dropped. Unmatched "Z" placeholders are replaced by "0" (zero). This yields an output string of "$23.00".

Negative numbers

You format negative numbers in the same way that you format positive numbers. The negative sign is automatically inserted at the beginning of the formatted string, before any literal text or digits (with one exception, explained below).

Controlling formatting width

How wide (that is, how many characters) will the string created by a formatting template be? Templates fall into three categories:

  • unlimited width
  • limited width
  • fixed width

An unlimited width template will expand to fit any number no matter how large. A template is of unlimited width to the left of the decimal if it begins with a grouping character (thus creating a repeating group), or if it begins with the placeholder "W", which stands for an infinite number of "N" placeholders. You cannot create an unlimited width template to the right of the decimal. BCD numbers support only 16 decimal places.

When we say "at the beginning/end" of the template, we mean exclusive of literal text, such as "$". Literal text at the beginning or end of a template does not affect its width characteristics.

A limited width template will not grow any larger than the number of specified placeholders. A template is of limited width to the left of the decimal if it begins with one or more "N" placeholders. It is of limited width to the right of the decimal if it ends with one or more "N" placeholders.

A fixed width template will always produce a string of the same width no matter how large or small the number being formatted. A template is of fixed width to the left of the decimal if it begins with one or more "B" or "Z" placeholders. It is of fixed width to the right of the decimal if it ends with one or more "B" or "Z" placeholders. A "B" placeholder is replaced with a space (blank) if not matched by a digit.

Numbers that overflow a fixed or limited width template on the left of the decimal point cause an error. Numbers that overflow the template on the right side of the decimal point are rounded with ties resolved away from zero (that is, 5 is rounded up, not down).

The format template "<$,NNZ.ZZ>" is infinitely wide on the left of the decimal and of fixed width on the right of the decimal. This means that it will display an infinitely large number of dollars and will round fractions to the nearest cent.

The minus sign on a negative number is treated like a digit for template formatting. That is, it requires an available placeholder. In limited width and fixed width templates, an error will occur if no placeholder is available for the minus sign.

The minus sign consumes a placeholder and then migrates to the left of any literal text in the format. You do not have to supply a placeholder to the left of literal text for the minus sign to go there.

In fixed width formats preceded by literal text, the minus sign behaves differently. Instead of migrating to the left of the literal text, it replaces the leftmost unmatched "B" placeholder. This keeps the literal text aligned on the left side of the output. If no unmatched "B" placeholder is available, an error occurs.

Rounding

If you specify a template that is of fixed or limited width to the right of the decimal, numbers will be rounded to fit the template. Thus the code:

  process
     local bcd total
  
     set total to 9.9999999999999999
     output "<WZ.NNNNNNNNNNNNNNNN>" % total || "%n"
     output "<WZ.ZZZ>" % total || "%n"
     output "<WN.NNN>" % total || "%n"

will produce the following output:

  9.9999999999999999
  10.000
  10

The rounding provided by a template is equivalent to calling the round function with a precision specifier that has the same number of decimal places as the template has placeholders after the decimal place. Thus the two output statements in the following program output the same string:

  process
     local bcd total
  
     set total to 9.9999999999999999
     output <W.NNN> % total || "%n"
     output <W.NNNNNNNNNNNNNNNN> % round total by bcd 0.001 || "%n"

Command-based formatting

To format a BCD as a string of digits, use the "d" % string. The following program outputs the string "-120.65":

  process
      local bcd quantity
      set quantity to -120.65
      output "d" % quantity

You can pad the output string with spaces on the right side using the "f" format modifier along with a number indicating the total size of the output string. The following code will output [-53.17 ].

  process
      local bcd quantity
      set quantity to -53.17
      output "[" || "8fd" % quantity || "]"

If you specify a number smaller than the number of digits in the number, digits to the right of the decimal will be truncated as necessary to fit the field width specified. If the number of digits in the number is greater than the field width after all digits to the right of the decimal point (and the decimal point itself) have been truncated, the whole number portion will be printed in full.

You can force the padding to occur on the left instead of the right by adding the "k" format modifier. The following code outputs [ -53.17].

  process
      local bcd quantity
      set quantity to -53.17
      output "[" || "8fkd" % quantity || "]"

Note that command-based formatting does not provide a means of rounding fractional amounts for display. To display fewer than 16 digits to the right of the decimal point, you should round the number first using the round function:

  process
      local bcd quantity
      set quantity to 9.56789
      output "d" % round quantity by bcd 0.001

The code above will output:

  9.568