The decNumber Library, version 3.36
Copyright (c) IBM Corporation, 2006. All rights reserved. ©
6 Jul 2006
[previous | contents | next]

User’s Guide

To use the decNumber library efficiently it is best to first convert the numbers you are working with from their coded representation into decNumber format, then carry out calculations on them, and finally convert them back into the desired coded format.

Conversions to and from the decNumber format are fast; they are usually faster than even the simplest calculations (x=x+1, for example). Therefore, in general, the cost of conversions is small compared to that of calculation.

The coded formats currently provided for in the library are

The remainder of this section illustrates the use of these coded formats in conjunction with the core decContext and decNumber modules by means of examples.

Notes on running the examples

  1. All the examples are written conforming to ANSI C, except that they use ‘line comment’ notation (comments starting with //) from BCPL and C++ for more concise commentary. Most C compilers support this; if not, a short script can be used to convert the line comments to traditional block comments (/* ... */).
  2. The header files and Example 6 use the standard integer types from stdint.h described in the ANSI C99 standard (ISO/IEC 9899:1999). If your C compiler does not supply stdint.h, the following will suffice:
      /* stdint.h -- some standard integer types from C99 */
      typedef unsigned char  uint8_t;
      typedef          char   int8_t;
      typedef unsigned short uint16_t;
      typedef          short  int16_t;
      typedef unsigned int   uint32_t;
      typedef          int    int32_t;
      typedef unsigned long long uint64_t;
      typedef          long long int64_t;
    
    You may need to change these if (for example) the int type in your compiler does not describe a 32-bit integer. If there are no 64-bit integers available with your compiler, set the DECUSE64 tuning parameter to 0; the last two typedefs above are then not needed.
  3. One aspect of the examples is implementation-defined. It is assumed that the default handling of the SIGFPE signal is to end the program. If your implementation ignores this signal, the lines with set.traps=0; would not be needed in the simpler examples.


Example 1 – simple addition

This example is a simple test program which can easily be extended to demonstrate more complicated operations or to experiment with the functions available.
   1.  // example1.c -- convert the first two argument words to decNumber,
   2.  // add them together, and display the result
   3.  
   4.  #define  DECNUMDIGITS 34           // work with up to 34 digits
   5.  #include "decNumber.h"             // base number library
   6.  #include <stdio.h>                 // for printf
   7.  
   8.  int main(int argc, char *argv[]) {
   9.    decNumber a, b;                  // working numbers
  10.    decContext set;                  // working context
  11.    char string[DECNUMDIGITS+14];    // conversion buffer
  12.  
  13.    if (argc<3) {                    // not enough words
  14.      printf("Please supply two numbers to add.\n");
  15.      return 1;
  16.      }
  17.    decContextDefault(&set, DEC_INIT_BASE); // initialize
  18.    set.traps=0;                     // no traps, thank you
  19.    set.digits=DECNUMDIGITS;         // set precision
  20.  
  21.    decNumberFromString(&a, argv[1], &set);
  22.    decNumberFromString(&b, argv[2], &set);
  23.    decNumberAdd(&a, &a, &b, &set);            // a=a+b
  24.    decNumberToString(&a, string);
  25.    printf("%s + %s => %s\n", argv[1], argv[2], string);
  26.    return 0;
  27.    } // main
This example is a complete, runnable program. In later examples we’ll leave out some of the ‘boilerplate’, checking, etc., but this one should compile and be usable as it stands.

Lines 1 and 2 document the purpose of the program.

Line 4 sets the maximum precision of decNumbers to be used by the program, which is used by the embedded header file in line 5 (and also elsewhere in this program).

Line 6 includes the C library for input and output, so we can use the printf function. Lines 8 through 11 start the main function, and declare the variables we will use. Lines 13 through 16 check that enough argument words have been given to the program.

Lines 17–19 initialize the decContext structure, turn off error signals, and set the working precision to the maximum possible for the size of decNumbers we have declared.

Lines 21 and 22 convert the first two argument words into numbers; these are then added together in line 23, converted back to a string in line 24, and displayed in line 25.

Note that there is no error checking of the arguments in this example, so the result will be NaN (Not a Number) if one or both words is not a number. Error checking is introduced in Example 3.


Example 2 – compound interest

This example takes three parameters (initial amount, interest rate, and number of years) and calculates the final accumulated investment. For example:
  100000 at 6.5% for 20 years => 352364.51
The heart of the program is:
   1.  decNumber one, mtwo, hundred;                   // constants
   2.  decNumber start, rate, years;                   // parameters
   3.  decNumber total;                                // result
   4.  decContext set;                                 // working context
   5.  char string[DECNUMDIGITS+14];                   // conversion buffer
   6.  
   7.  decContextDefault(&set, DEC_INIT_BASE);         // initialize
   8.  set.traps=0;                                    // no traps
   9.  set.digits=25;                                  // precision 25
  10.  decNumberFromString(&one,       "1", &set);     // set constants
  11.  decNumberFromString(&mtwo,     "-2", &set);
  12.  decNumberFromString(&hundred, "100", &set);
  13.  
  14.  decNumberFromString(&start, argv[1], &set);     // parameter words
  15.  decNumberFromString(&rate,  argv[2], &set);
  16.  decNumberFromString(&years, argv[3], &set);
  17.  
  18.  decNumberDivide(&rate, &rate, &hundred, &set);  // rate=rate/100
  19.  decNumberAdd(&rate, &rate, &one, &set);         // rate=rate+1
  20.  decNumberPower(&rate, &rate, &years, &set);     // rate=rate**years
  21.  decNumberMultiply(&total, &rate, &start, &set); // total=rate*start
  22.  decNumberRescale(&total, &total, &mtwo, &set);  // two digits please
  23.  
  24.  decNumberToString(&total, string);
  25.  printf("%s at %s%% for %s years => %s\n",
  26.         argv[1], argv[2], argv[3], string);
  27.  return 0;
These lines would replace the content of the main function in Example 1 (adding the check for the number of parameters would be advisable).

As in Example 1, the variables to be used are first declared and initialized (lines 1 through 12), with the working precision being set to 25 in this case. The parameter words are converted into decNumbers in lines 14–16.

The next four function calls calculate the result; first the rate is changed from a percentage (e.g., 6.5) to a per annum rate (1.065). This is then raised to the power of the number of years (which must be a whole number), giving the rate over the total period. This rate is then multiplied by the initial investment to give the result.

Next (line 22) the result is rescaled so it will have only two digits after the decimal point (an exponent of -2), and finally (lines 24–26) it is converted to a string and displayed.


Example 3 – passive error handling

Neither of the previous examples provide any protection against invalid numbers being passed to the programs, or against calculation errors such as overflow. If errors occur, therefore, the final result will probably be NaN or infinite (decNumber result structures are always valid after an operation, but their value may not be useful).

One way to check for errors would be to check the status field of the decContext structure after every decNumber function call. However, as that field accumulates errors until cleared deliberately it is often more convenient and more efficient to delay the check until after a sequence is complete.

This passive checking is easily added to Example 2. Replace lines 14 through 22 in that example with (the original lines repeated here are unchanged):

   1.  decNumberFromString(&start, argv[1], &set);     // parameter words
   2.  decNumberFromString(&rate,  argv[2], &set);
   3.  decNumberFromString(&years, argv[3], &set);
   4.  if (set.status) {
   5.    printf("An input argument word was invalid [%s]\n",
   6.           decContextStatusToString(&set));
   7.    return 1;
   8.    }
   9.  decNumberDivide(&rate, &rate, &hundred, &set);  // rate=rate/100
  10.  decNumberAdd(&rate, &rate, &one, &set);         // rate=rate+1
  11.  decNumberPower(&rate, &rate, &years, &set);     // rate=rate**years
  12.  decNumberMultiply(&total, &rate, &start, &set); // total=rate*start
  13.  decNumberRescale(&total, &total, &mtwo, &set);  // two digits please
  14.  if (set.status & DEC_Errors) {
  15.    set.status &= DEC_Errors;                     // keep only errors
  16.    printf("Result could not be calculated [%s]\n",
  17.           decContextStatusToString(&set));
  18.    return 1;
  19.    }
Here, in the if statement starting on line 4, the error message is displayed if the status field of the set structure is non-zero. The call to decContextStatusToString in line 6 returns a string which describes a set status bit (probably ‘Conversion syntax’).

In line 14, the test is augmented by anding the set.status value with DEC_Errors. This ensures that only serious conditions trigger the message. In this case, it is possible that the DEC_Inexact and DEC_Rounded conditions will be set (if an overflow occurred) so these are cleared in line 15.

With these changes, messages are displayed and the main function ended if either a bad input parameter word was found (for example, try passing a non-numeric word) or if the calculation could not be completed (e.g., try a value for the third argument which is not an integer).[1] 


Example 4 – active error handling

The last example handled errors passively, by testing the context status field directly. In this example, the C signal mechanism is used to handle traps which are raised when errors occur.

When one of the decNumber functions sets a bit in the context status, the bit is compared with the corresponding bit in the traps field. If that bit is set (is 1) then a C Floating-Point Exception signal (SIGFPE) is raised. At that point, a signal handler function (previously identified to the C runtime) is called.

The signal handler function can either simply log or report the trap and then return (and execution will continue as though the trap had not occurred) or – as in this example – it can call the C longjmp function to jump to a previously preserved point of execution.

Note that if a jump is used, control will not return to the code which called the decNumber function that raised the trap, and so care must be taken to ensure that any resources in use (such as allocated memory) are cleaned up appropriately.

To create this example, modify the Example 1 code this time, by first removing line 18 (set.traps=0;). This will leave the traps field with its default setting, which has all the DEC_Errors bits set, hence enabling traps for any of those conditions. Then insert after line 6 (before the main function):

   1.  #include <signal.h>                // signal handling
   2.  #include <setjmp.h>                // setjmp/longjmp
   3.  
   4.  jmp_buf preserve;                  // stack snapshot
   5.  
   6.  void signalHandler(int sig) {
   7.    signal(SIGFPE, signalHandler);   // re-enable
   8.    longjmp(preserve, sig);          // branch to preserved point
   9.    }
Here, lines 1 and 2 include definitions for the C library functions we will use. Line 4 declares a global buffer (accessible to both the main function and the signal handler) which is used to preserve the point of execution to which we will jump after handling the signal.

Lines 6 through 9 are the signal handler. Line 7 re-enables the signal handler, as described below (in this example this is in fact unnecessary as we will be ending the program immediately). This is normally needed as handlers are disabled on entry, and need to be re-enabled if more than one trap is to be handled.

Line 8 jumps to the point preserved when the program starts up (in the next code insert). The value, sig, which the signal handler receives is passed to the preserved code. In this example, sig always has the value SIGFPE, but in a more complicated program the same signal handler could be used to handle other signals, too.

The next segment of code is inserted after line 11 of Example 1 (just after the existing declarations):

   1.  int value;                       // work variable
   2.  
   3.  signal(SIGFPE, signalHandler);   // set up signal handler
   4.  value=setjmp(preserve);          // preserve and test environment
   5.  if (value) {                     // (non-0 after longjmp)
   6.    set.status &= DEC_Errors;      // keep only errors
   7.    printf("Signal trapped [%s].\n", decContextStatusToString(&set));
   8.    return 2;
   9.    }
Here, a work variable is declared in line 1 and the signal handler function is registered (identified to the C run time) in line 3. The call to the signal function identifies the signal to be handled (SIGFPE) and the function (signalHandler) that will be called when the signal is raised, and enables the handler.

Next, in line 4, the setjmp function is called. On its first call, this saves the current point of execution into the preserve variable and then returns 0. The following lines (5–8) are then not executed and execution of the main function continues as before.

If a trap later occurs (for example, if one of the arguments is not a number) then the following takes place:

  1. the SIGFPE signal is raised by the decNumber library
  2. the signalHandler function is called by the C run time with argument SIGFPE
  3. the function re-enables the signal, and then calls longjmp
  4. this in turn causes the execution stack to be ‘unwound’ to the point which was preserved in the initial call to setjmp
  5. the setjmp function then returns, with the (non-0) value passed to it in the call to longjmp
  6. the test in line 5 then succeeds, so line 6 clears any informational status bits in the status field in the context structure which was given to the decNumber routines and line 7 displays a message, using the same structure
  7. finally, in line 8, the main function is ended by the return statement.
Of course, different behaviors are possible both in the signal handler, as already noted, and after the jump; the main program could prompt for new values for the input parameters and then continue as before, for example.


Example 5 – compressed formats

The previous examples all used decNumber structures directly, but that format is not necessarily compact and is machine-dependent. These attributes are generally good for performance, but are less suitable for the storage and exchange of numbers.

The decimal32, decimal64, and decimal128 forms are provided as efficient, machine-independent formats used for storing numbers of up to 7, 16 or 34 decimal digits respectively, in 4, 8, or 16 bytes. These formats are similar to, and are used in the same manner as, the C float and double data types.

Here’s an example program. Like Example 1, this is runnable as it stands, although it’s recommended that at least the argument count check be added.

   1.  // example5.c -- decimal64 conversions
   2.  #include "decimal64.h"             // decimal64 and decNumber library
   3.  #include <stdio.h>                 // for (s)printf
   4.  
   5.  int main(int argc, char *argv[]) {
   6.    decimal64 a;                     // working decimal64 number
   7.    decNumber d;                     // working number
   8.    decContext set;                  // working context
   9.    char string[DECIMAL64_String];   // number->string buffer
  10.    char hexes[25];                  // decimal64->hex buffer
  11.    int i;                           // counter
  12.  
  13.    decContextDefault(&set, DEC_INIT_DECIMAL64); // initialize
  14.  
  15.    decimal64FromString(&a, argv[1], &set);
  16.    // lay out the decimal64 as eight hexadecimal pairs
  17.    for (i=0; i<8; i++) {
  18.      sprintf(&hexes[i*3], "%02x ", a.bytes[i]);
  19.      }
  20.    decimal64ToNumber(&a, &d);
  21.    decNumberToString(&d, string);
  22.    printf("%s => %s=> %s\n", argv[1], hexes, string);
  23.    return 0;
  24.    } // main
Here, the #include on line 2 not only defines the decimal64 type, but also includes the decNumber and decContext header files. Also, if DECNUMDIGITS has not already been defined, the decimal64.h file sets it to 16 so that any decNumbers declared will be exactly the right size to take any decimal64 without rounding.

The declarations in lines 6–11 create three working structures and other work variables; the decContext structure is initialized in line 13 (here, set.traps is 0).

Line 15 converts the input argument word to a decimal64 (with a function call very similar to decNumberFromString). Note that the value would be rounded if the number needed more than 16 digits of precision.

Lines 16–19 lay out the decimal64 as eight hexadecimal pairs in a string, so that its encoding can be displayed.

Lines 20–22 show how decimal64 numbers are used. First the decimal64 is converted to a decNumber, then arithmetic could be carried out, and finally the decNumber is converted back to some standard form (in this case a string, so it can be displayed in line 22). For example, if the input argument were ‘79’, the following would be displayed:

  79 => 22 38 00 00 00 00 00 79 => 79
The decimal32 and decimal128 forms are used in exactly the same way, for working with up to 7 or up to 34 digits of precision respectively. These forms have the same constants and functions as decimal64 (with the obvious name changes).

Like decimal64.h, the decimal32 and decimal128 header files define the DECNUMDIGITS constant to either 7 or 34 if it has not already been defined.


Example 6 – Packed Decimal numbers

This example reworks Example 2, starting and ending with Packed Decimal numbers. First, lines 4 and 5 of Example 1 (which Example 2 modifies) are replaced by the line:
   1.  #include "decPacked.h"
Then the following declarations are added to the main function:
   1.  uint8_t startpack[]={0x01, 0x00, 0x00, 0x0C};   // investment=100000
   2.  int32_t startscale=0;
   3.  uint8_t ratepack[]={0x06, 0x5C};                // rate=6.5%
   4.  int32_t ratescale=1;
   5.  uint8_t yearspack[]={0x02, 0x0C};               // years=20
   6.  int32_t yearsscale=0;
   7.  uint8_t respack[16];                            // result, packed
   8.  int32_t resscale;                               // ..
   9.  char    hexes[49];                              // for packed->hex
  10.  int     i;                                      // counter
The first three pairs declare and initialize the three parameters, with a Packed Decimal byte array and associated scale for each. In practice these might be read from a file or database. The fourth pair is used to receive the result. The last two declarations (lines 9 and 10) are work variables used for displaying the result.

Next, in Example 2, line 5 is removed, and lines 14 through 26 are replaced by:

   1.  decPackedToNumber(startpack, sizeof(startpack), &startscale, &start);
   2.  decPackedToNumber(ratepack,  sizeof(ratepack),  &ratescale,  &rate);
   3.  decPackedToNumber(yearspack, sizeof(yearspack), &yearsscale, &years);
   4.  
   5.  decNumberDivide(&rate, &rate, &hundred, &set);  // rate=rate/100
   6.  decNumberAdd(&rate, &rate, &one, &set);         // rate=rate+1
   7.  decNumberPower(&rate, &rate, &years, &set);     // rate=rate**years
   8.  decNumberMultiply(&total, &rate, &start, &set); // total=rate*start
   9.  decNumberRescale(&total, &total, &mtwo, &set);  // two digits please
  10.  
  11.  decPackedFromNumber(respack, sizeof(respack), &resscale, &total);
  12.  
  13.  // lay out the total as sixteen hexadecimal pairs
  14.  for (i=0; i<16; i++) {
  15.    sprintf(&hexes[i*3], "%02x ", respack[i]);
  16.    }
  17.  printf("Result: %s (scale=%d)\n", hexes, resscale);
Here, lines 1 through 3 convert the Packed Decimal parameters into decNumber structures. Lines 5-9 calculate and rescale the total, as before, and line 11 converts the final decNumber into Packed Decimal and scale. Finally, lines 13-17 lay out and display the result, which should be:
  Result: 00 00 00 00 00 00 00 00 00 00 00 03 52 36 45 1c  (scale=2)
Note that the number is right-aligned, with a sign nibble.
Footnotes:
[1] Of course, in a user-friendly application, more detailed and specific error messages are appropriate. But here we are demonstrating error handling, not user interfaces.

[previous | contents | next]