BCD data type

The BCD (binary coded decimal) data type allows you to store numbers in binary coded decimal form. This means that the numbers behave exactly like the decimal numbers you learned about in grade school and that decimal fractions behave exactly like the decimal fractions you learned about in grade eight.

BCD numbers are unlimited in size. They have an accuracy of 16 places past the decimal point. Fractional results of BCD calculations which result in more than 16 digits past the decimal point are truncated to 16 digits.

BCD numbers are particularly appropriate for commerce applications. They eliminate the problems of rounding errors inherent in using floating point numbers for financial calculations.

BCD numbers are generally superior to floating point numbers for most applications. Why should you use BCD numbers rather than floating point numbers? There are three principal differences.

BCD data type formatting lets you display prices with dollar signs or British pound signs, and lets you control how decimals are displayed and whether large numbers are separated every three digits by commas or spaces or left unchanged.

Base 2 versus base 10

Floating point numbers are represented internally as binary (base 2) numbers. They provide accurate representation of fractional numbers that are powers of 2 (1/2, 1/4, 1/8, 1/16, and so forth), but they do not provide accurate representation of fractions that are powers of 10 (1/10, 1/100, 1/1000). Any fraction that can be precisely represented in base 2 can be precisely represented in base 10, but not vice versa. (There are, of course, many fractions that cannot be precisely represented in either base 2 or base 10—1/3 for example.)

Limited size versus unlimited size

Floating point numbers are of a limited size and are represented by a fixed number of bytes of memory. BCD numbers, as implemented by the OmniMark BCD library, are of unlimited size.

Floating point versus fixed point

Floating point numbers, as their name implies, have a floating decimal point. That is, floating point numbers have a fixed number of significant bits which are distributed between the whole number portion and the fractional portion of the number. The larger the whole number portion of the number, the fewer bits are available for the fractional part.

BCD numbers, as implemented by the OmniMark BCD library, have a fixed number of digits past the decimal point. The limit is 16 digits. Thus, BCD numbers give you unlimited digits to the left of the decimal point and 16 digits to the right of the decimal point.

Mixing BCD and integer values

You can mix integer variables and BCD variables in mathematical expressions. Thus, you can write:

  process
     local bcd price
     local bcd total
     local integer quantity
  set total to quantity * price

You can also choose to use the BCD data type for integer values:

  local bcd price
  local bcd quantity
  local bcd total
  
  set total to quantity * price

Note that if you perform an operation on two integers and assign the result to a BCD, the operation will be done as an integer operation and the result will be coerced to a BCD. Thus the following code will fail, even though a BCD can hold the result of 1000000 * 2000000:

  process
     local integer large initial {1000000}
     local integer larger initial {2000000}
     local bcd largest
  
     set largest to bcd (large * larger)

In this case, the result of the integer operation large * larger will overflow before the coercion to BCD. The correct way to code this operation is to force one of the operands to BCD before the operation is performed. This causes the operation to be performed as a BCD operation, returning a BCD value:

  process
     local integer large initial {1000000}
     local integer larger initial {2000000}
     local bcd largest
  
     set largest to bcd large * larger

Supported operators

You can use the following operators with BCD numbers:

Handling BCD errors

In the event of an error in a calculation, the BCD library will throw a #external-exception. You can catch external exceptions and take the appropriate action:

  import "ombcd.xmd" unprefixed
  global bcd total initial {111.03}
  process
      submit "-332.33A"
      find ("-"? (digit+ ".")? digit+ letter?) => decimal-number
        set total to total + bcd decimal-number
        output "Total = " || "<$,NNZ.ZZ>" % total
  
     catch #external-exception
      identity error-code
      message error-message
      location error-location
        do scan error-code
           match "BCD"
              log-message ("Error "
                       ||  error-code
                       ||  " "
                       ||  error-message
                       ||  " at "
                       ||  error-location
                       ||  ".%n")
           else
              rethrow
        done
  ; Output: "Error BCD0002 '-332.33A' is not a legal bcd number. at On line 7 in file
  ;          C:\omprogs\bcdcatch.xom.."

Related Topics