The decNumber Library, version 3.68
Copyright (c) IBM Corporation, 2010. All rights reserved. ©
23 Jan 2010
[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 strings or another coded representation into decNumber format, then carry out calculations on them, and finally convert them back into the desired string or coded format.

Conversions to and from the decNumber format are fast; they are usually faster than all but 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

However, when arbitrary-precision calculation is not required (that is, up to 34 digits of precision is all that is required) it is even more efficient to use one of the decFloats modules for arithmetic and other operations. The decFloats modules work directly from the decimal-encoded compressed formats and avoid the need for conversions to and from the decNumber format. Tables comparing the performance of the decFloats modules with decNumber can be found in Appendix A.

The remainder of this section illustrates the use of the coded formats and the decFloats modules 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 (/* ... */). Note that the decNumber header files use only block comments so do not require conversion.
  2. Some pieces of the decNumber package are sensitive to the whether the underlying platform is big-endian or little-endian; for a big-endian machine, set the DECLITEND tuning parameter to 0.
    The code provided for the first and seventh examples (example1.c and example7.c) includes a call to the decContextTestEndian routine, which will display a warning if DECLITEND is set incorrectly.
  3. 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.
  4. 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
   4.  #define  DECNUMDIGITS 34           // work with up to 34 digits
   5.  #include "decNumber.h"             // base number library
   6.  #include <stdio.h>                 // for printf
   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
  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
  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
   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);
  14.  decNumberFromString(&start, argv[1], &set);     // parameter words
  15.  decNumberFromString(&rate,  argv[2], &set);
  16.  decNumberFromString(&years, argv[3], &set);
  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
  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 provides 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
   4.  jmp_buf preserve;                  // stack snapshot
   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
   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, 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
   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
  13.    decContextDefault(&set, DEC_INIT_DECIMAL64); // initialize
  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 on a big-endian machine:

  79 => 22 38 00 00 00 00 00 79 => 79
(On a little-endian machine the byte order would be reversed.)

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.

It is also possible to work with the decimal128 (etc.) formats directly, without converting to and from the decNumber format; this is much faster when only the fixed-size formats are needed. Example 7 shows how to use the decQuad module for calculations in the 128-bit format.

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);
   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
  11.  decPackedFromNumber(respack, sizeof(respack), &resscale, &total);
  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=%ld)\n", hexes, (long int)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.

Example 7 – Using the decQuad module

This example reworks Example 1, but using the decQuad module for all conversions and the arithmetic.
   1.  // example7.c -- using decQuad to add two numbers together
   3.  #include "decQuad.h"               // decQuad library
   4.  #include <stdio.h>                 // for printf
   6.  int main(int argc, char *argv[]) {
   7.    decQuad a, b;                    // working decQuads
   8.    decContext set;                  // working context
   9.    char string[DECQUAD String];     // number->string buffer
  11.    if (argc<3) {                    // not enough words
  12.      printf("Please supply two numbers to add.\.n");
  13.      return 1;
  14.      }
  15.    decContextDefault(&set, DEC INIT DECQUAD); // initialize
  17.    decQuadFromString(&a, argv[1], &set);
  18.    decQuadFromString(&b, argv[2], &set);
  19.    decQuadAdd(&a, &a, &b, &set);    // a=a+b
  20.    decQuadToString(&a, string);
  22.    printf("%s + %s => %s\n", argv[1], argv[2], string);
  23.    return 0;
  24.    } // main
This example is a complete, runnable program. Like Example 1, it takes two argument words, converts them to a decimal format (in this case decQuad, the 34-digit format), adds them, and converts the result back to a string for display.

Line 3 includes the decQuad header file. This in turn includes the other necessary header, decContext. The context variable set is used to set the rounding mode for the conversions from string and for the add, and its status field is used to report any errors (not checked in this example). No other field in the context is used.

To compile and run this, only the files example7.c, decContext.c, and decQuad.c are needed.

To use the 16-digit format instead of the 34-digit format, change decQuad to decDouble and QUAD to DOUBLE in the example. Note that in this case the file decQuad.c is still needed (must be compiled), because decDouble requires decQuad.

Example 8 – Using decQuad with decNumber

This example shows how the decNumber and decQuad modules can be mixed, in this case to raise one number to the power of another. (In this case, the use of the decQuad module could be avoided – this is just to demonstrate how to use the two modules together.)
   1.  // example8.c -- using decQuad with the decNumber module
   3.  #include "decQuad.h"               // decQuad library
   4.  #include "decimal128.h"            // interface to decNumber
   5.  #include <stdio.h>                 // for printf
   7.  int main(int argc, char *argv[]) {
   8.    decQuad a;                       // working decQuad
   9.    decNumber numa, numb;            // working decNumbers
  10.    decContext set;                  // working context
  11.    char string[DECQUAD String];     // number->string buffer
  13.    if (argc<3) {                    // not enough words
  14.      printf("Please supply two numbers for power(2*a, b).\n");
  15.      return 1;
  16.      }
  17.    decContextDefault(&set, DEC INIT DECQUAD); // initialize
  19.    decQuadFromString(&a, argv[1], &set);      // get a
  20.    decQuadAdd(&a, &a, &a, &set);              // double a
  21.    decQuadToNumber(&a, &numa);                // convert to decNumber
  22.    decNumberFromString(&numb, argv[2], &set);
  23.    decNumberPower(&numa, &numa, &numb, &set); // numa=numa**numb
  24.    decQuadFromNumber(&a, &numa, &set);        // back via a Quad
  25.    decQuadToString(&a, string);               // ..
  27.    printf("power(2*%s, %s) => %s\n", argv[1], argv[2], string);
  28.    return 0;
  29.    } // main
Here, the decimal128 module is used as a ‘proxy’ between the decNumber and decQuad formats. The decimal128 and decQuad structures are identical (except in name) so pointers to the structures can safely be cast from one to the other. The decQuadToNumber and decQuadFromNumber functions are in fact macros which cast the data pointer and then use the decimal128ToNumber or decimal128FromNumber function to effect the conversion. Using a proxy in this way avoids any dependencies between decQuad and decNumber.

Note that the same decContext structure (set) is used for both decQuad and decNumber function calls. decQuad uses only the round and status fields, but decNumber also needs the other fields. All the fields are initialized by the call to decContextDefault.

The inclusion of decimal128.h also sets up the DECNUMDIGITS required and includes decNumber.h. The decimal128 module requires decimal64 (for shared code and tables), so the full list of files to compile for this example is: example8.c, decContext.c, decQuad.c, decNumber.c, decimal128.c, and decimal64.c.

[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]