DecFP_detailled

Decimal floating-point class

Decimal floating-point types are likely to be part of a future C and C++ standard.

Here is a simple wrapper class for Mike Cowlishaw’s decNumber C Library, written in C++.

It is currently in an early design stage (just a first try, the design is far from perfect, it is incomplete and notations are sometimes inconsistent). But most of its operators work as they should do, and most of Mikes testcases pass successfully. The function DecimalFloat_Testcases(“d:\\cpp.boo\\decnumber\\decnumberclass\\decTest\\”) in my test program test_rk reads all the testcases from Mikes decTest files and logs each testcase in a file (bcb files were created with Borland C++Builder 6, gcc with MinGW g++, and msc with MS Visual Studio .Net 2003). With “grep “###number” *.log” you get the summary lines:

File abs.decTest_bcb.log:    ###number of tests passed=87  failed=2
File abs.decTest_gcc.log:    ###number of tests passed=87  failed=2
File abs.decTest_msc.log:    ###number of tests passed=87  failed=2
File add.decTest_bcb.log:    ###number of tests passed=773 failed=45
File add.decTest_gcc.log:    ###number of tests passed=773 failed=46
File add.decTest_msc.log:    ###number of tests passed=773 failed=45
...

Detailled example usage:

/******** Remark by Richard Kaiser *********************************************
*
*   Decimal floating-point number wrapper class, based on
*   Mike Cowlishaw's decNumber C Library (http://www2.hursley.ibm.com/decimal/).
*
*   To use this class, follow the steps 1., 2. and 3.
*
*   Copyright Richard Kaiser, 2004
*
*   If you find any bugs, please let me know: rk@rkaiser.de
*   May 8, 2004
********** End of remark by Richard Kaiser ************************************/#if __BCPLUSPLUS__ // for Borland
#pragma hdrstop
#pragma argsused
#endif/******** Remark by Richard Kaiser *********************************************
*
* Step 1: Define one and only one of these macros:
*
*       _DECIMAL128_, _DECIMAL64_, _DECIMAL32_, _DECIMAL_ARBITRARY_
*
*     It is not possible, to define different decimal floating-point types in
*     one program. Therefore you have to define one and only one of these macros.
*
* Step 2: Install Mike Cowlishaw's decNumber C Library to some directory.
*
* Step 3: Change the pathname in the next #include statments to the directory
*     with the decNumber C Library.
*
* Thats it. After these steps you can #include my decFloat library. It provides
* the wrapper class with the decimal floating-point class DecFloat_.
*
********** End of remark by Richard Kaiser ************************************/

#define _DECIMAL128_
// #define _DECIMAL64_
// #define _DECIMAL32_
// #define _DECIMAL_ARBITRARY_

/******** Remark by Richard Kaiser *********************************************
*
* Besides changing the #include-pathnames, you don't have to make any changes
* to the next preprocessor statements.
*
********** End of remark by Richard Kaiser ************************************/

// ??? DECALLOC flag set to 1?  DECCHECK might be useful, too. Set to 1 in decnumberlocal 51/52

#ifdef _DECIMAL128_
#elif defined(_DECIMAL64_)
#elif defined (_DECIMAL32_)
#elif defined (_DECIMAL_ARBITRARY_)
#else
#error You have to define one (and only one of) _DECIMAL128_, _DECIMAL64_, _DECIMAL32_, _DECIMAL_ARBITRARY_
#endif

// These statements provide the 128 bit decimal floating-point class DedFloat_
#ifdef _DECIMAL128_
#include "decNumber\decimal128.h"
#include "DecFloat.h"
#endif

// These statements provide the 64 bit decimal floating-point class DedFloat_
#ifdef _DECIMAL64_
#include "decNumber\decimal64.h"
#include "DecFloat.h"
#endif

// These statements provide the 32 bit decimal floating-point class DedFloat_
#ifdef _DECIMAL32_
#include "decNumber\decimal32.h"
#include "DecFloat.h"
#endif

/******************************************************************************
// You can also define arbitray precision types:
*******************************************************************************/
#ifdef _DECIMAL_ARBITRARY_
#define  DECNUMDIGITS 100           // work with up to 100 digits, you can use any other number
#include "decNumber\decimal128.h"
#include "DecFloat.h"
#endif

/******** Remark by Richard Kaiser *********************************************
*
* Step 3: You have to include Mike Cowlishaw's decNumber C Library.
*
*     All you have to do here, is to change the pathname to the directory
*     with the decNumber C Library.
*
********** End of remark by Richard Kaiser ************************************/

#include "decNumber\decnumber.c"
#include "decnumber\deccontext.c"

/******** Remark by Richard Kaiser *********************************************
*
* Note:
*
* In the C/C++ standard, names for these types have to be specified. In
* discussions the names "decimal32", "decimal64" and "decimal128" are
* often used. I would prefer to use names like "decimalfp32", "decimalfp64" and
* "decimalfp128" that express explicitly that these types are floating-point
* types.
*
* The word "decimal" is often used in context with integer types. Decimal
* floating-point types are completely different from integral types. The
* names of these types should express this to avoid confusions.
*/

#ifdef _DECIMAL128_
typedef DecFloat_ decimalfp128; // I would prefer this name -- Richard
typedef DecFloat_ Decimal128;   // alternative name
typedef DecFloat_ DecimalFP128; // alternative name
#elif defined(_DECIMAL64_)
typedef DecFloat_ decimalfp64;  // I would prefer this name -- Richard
typedef DecFloat_ Decimal64;    // alternative name
typedef DecFloat_ DecimalFP64;  // alternative name
#elif defined (_DECIMAL32_)
typedef DecFloat_ decimalfp32;  // I would prefer this name -- Richard
typedef DecFloat_ Decimal32;    // alternative name
typedef DecFloat_ DecimalFP32;  // alternative name
#elif defined (_DECIMAL_ARBITRARY_)
typedef DecFloat_ Decimal_xxx;
typedef DecFloat_ DecimalFP_xxx;
typedef DecFloat_ decimalfp_xxx;
#else
#error You have to define one (and only one of) _DECIMAL128_, _DECIMAL64_, _DECIMAL32_, _DECIMAL_ARBITRARY_
#endif
/*
********** End of remark by Richard Kaiser ************************************/

/******** Remark by Richard Kaiser *********************************************
*
*  After the steps 1., 2. and 3. you have one of the decimal floating-point types
*  at your disposal. For example usage, see the main program.
*
*  The functions DecimalFloat_test_file and DecimalFloat_Testcases from
*  MCTestcases.cpp test Mike Cowlishaw's testcases. The functions prior to
*  these are helper functions.
*
*  DecimalFloat_Testcases creates files with a ".log" extension. These logfiles
*  log every test case as well as an additional line that states if a test was
*  passed. A final line with "###number" at the end of the logfile summarizes
*  the number of passed and failed test cases. You can get an overview about the
*  total lines with

c:\>grep "###number" decTest\*.log

File decTest\abs.decTest_bcb.log:
###number of tests passed=47  failed=42
File decTest\abs.decTest_gcc.log:
###number of tests passed=47  failed=42
File decTest\add.decTest_bcb.log:
###number of tests passed=277  failed=542
File decTest\add.decTest_gcc.log:
###number of tests passed=282  failed=537
File decTest\base.decTest_gcc.log:
###number of tests passed=354  failed=719
File decTest\clamp.decTest_bcb.log:
###number of tests passed=69  failed=54
...

*  All tests were done with Borland C++Builder 6, MS Visual Studio .Net and
*  MinGW g++ (gcc version 3.3.3), e.g.
*
*     g++ test_rk.cpp
*
********** End of remark by Richard Kaiser ************************************/

#include "MCTestcases.cpp" // Mike Cowlishaw's decTest testcases
#include "debug.cpp"
#include "Benchmarks.cpp"

#include <iomanip>

int main(int argc, char* argv[])
{
#ifdef _DECIMAL32_
cout<<"Decimal32:"<<endl;
unsigned int s32=sizeof(decimalfp32); // 20
cout<<"  sizeof(decimalfp32)="<<s32<<endl;
decimalfp32 x32="1234567890123456789012345", y32="1234567890123456789012145";   // 7 digits
cout<<"x="<<x32<<" y="<<y32<<" x*y="<<x32*y32<<endl;
cout<<endl;
#endif

#ifdef _DECIMAL64_
cout<<"Decimal64:"<<endl;
unsigned int s64=sizeof(decimalfp64);  // 24
cout<<"  sizeof(decimalfp64)="<<s64<<endl;
decimalfp64 x64="1234567890123456789012345", y64="1234567890123456789012345";   // 16 digits
cout<<"x="<<x64<<" y="<<y64<<" x*y="<<x64*y64<<endl;
cout<<endl;
#endif

#ifdef _DECIMAL128_
cout<<"Decimal128:"<<endl;
unsigned int s128=sizeof(decimalfp128);  // 32
cout<<"  sizeof(decimalfp128)="<<s128<<endl;
decimalfp128 x128="1234567890123456789012345", y128="1234567890123456789012345"; // 25 digits
decimalfp128 z128=x128*y128;                                                     // 34 digits
cout<<"x="<<x128<<" y="<<y128<<" x*y="<<x128*y128<<endl;
cout<<endl;
#endif

#ifdef _DECIMAL_ARBITRARY_
cout<<"Decimal_xxx:"<<endl;
unsigned int sxxx=sizeof(decimalfp_xxx);  // 64
cout<<"  sizeof(decimalfp_xxx)="<<sxxx<<endl;
decimalfp_xxx x_xxx="1234567890123456789012345", yxxx="1234567890123456789012345"; // 25 digits
decimalfp_xxx zxxx=x256*y256;                                                      // 49 digits
cout<<"x="<<x256<<" y="<<y256<<" x*y="<<x256*y256<<endl;
cout<<endl;
#endif

cout<<"Test_Decimal_Floats():"<<endl;
// decimal float class is DecFloat, defined in "DecFloat.h"
// DecFloats can be initialized with strings, ints and decimal float expressions:
#ifdef _DECIMAL128_
decimalfp128 d1="0.70",d2="1.05",d3=d1*d2;
#elif defined(_DECIMAL64_)
decimalfp64 d1="0.70",d2="1.05",d3=d1*d2;
#elif defined(_DECIMAL32_)
decimalfp32 d1="0.70",d2="1.05",d3=d1*d2;
#elif defined(_DECIMAL_ARBITRARY_)
decimalfp_xxx d1="0.70",d2="1.05",d3=d1*d2;
#else
#error You have to define one (and only one of) _DECIMAL128_, _DECIMAL64_, _DECIMAL32_, _DECIMAL_ARBITRARY_
#endif

//DecFloat d1="0.70",d2="1.05",d3=d1*d2;
// stream output for DecFloat expressions:
cout<<"d1="<<d1<<endl;
cout<<"d2="<<d2<<endl;
cout<<"d1*d2="<<d3<<endl;
int prec_old=Context.Precision();
Context.Precision(2);
cout<<"d1*d2="<<d1*d2<<endl;
Context.Precision(prec_old);
double d=0.7, f=1.05;
cout<<"double "<<d<<"*"<<f<<"="<<setw(6)<<setprecision(2)<<d*f<<endl;

// errors are reported by status flags
// DecContextStatusString() returns them as text
d1=0;
d2=d2/d1;
cout<<"After div by 0: DecContextStatusString()="<<DecContextStatusString()<<endl;
Context.ClearStatus();
d1="abc";
cout<<"After d1=\"abc\": DecContextStatusString()="<<DecContextStatusString()<<endl;
Context.ClearStatus();
cout<<"After ClearStatus(): "<<DecContextStatusString()<<endl;

// arithmetic expressions can be formed in the usual way:
int i1=17, i2=123, i3=123;
d1=i1, d2=i2,  d3=i3;
#ifdef _DECIMAL128_
decimalfp128 x=d1+(d1+d2*d3)-d1*d2;
#elif defined(_DECIMAL64_)
decimalfp64 x=d1+(d1+d2*d3)-d1*d2;
#elif defined(_DECIMAL32_)
decimalfp32 x=d1+(d1+d2*d3)-d1*d2;
#elif defined(_DECIMAL_ARBITRARY_)
decimalfp_xxx x=d1+(d1+d2*d3)-d1*d2;
#else
#error You have to define one (and only one of) _DECIMAL128_, _DECIMAL64_, _DECIMAL32_, _DECIMAL_ARBITRARY_
#endif
int      i=i1+(i1+i2*i3)-i1*i2;
cout<<x<<"=="<<i<<endl; // can't believe it: they are really equal

// comparison operators
if (d1<d2)  cout<<"less"<<endl;
if (d1<=d2) cout<<"less equal"<<endl;
if (d1>d2)  cout<<"greater"<<endl;
if (d1>=d2) cout<<"greater equal"<<endl;
if (d1==d2) cout<<"equal"<<endl;
if (d1!=d2) cout<<"not equal"<<endl;

// assignments
d1=d2;     // of DecFloats
d1="1.23"; // of string literals
d1=17;     // of ints

{ // filestream output and input
ofstream f("c:\\dftest.txt");
f<<d1<<endl;
f<<d2<<endl;
}

// math epressions pow, max, min, sqrt
cout<<"max("<<d1<<","<<d2<<")="<<max(d1,d2)<<endl;
cout<<"min("<<d1<<","<<d2<<")="<<min(d1,d2)<<endl;
d2=2;
cout<<"sqrt("<<d2<<")="<<sqrt(d2)<<endl;
cout<<"pow("<<d1<<","<<d2<<")="<<pow(d1,d2)<<endl;

debug_specialcases();

// some benchmarks:
Benchmarks();

// pass the directory of the decnumber testcases:
DecimalFloat_Testcases("d:\\cpp.boo\\decnumber\\decnumberclass\\decTest\\");
// Most of Mike Cowlishaw's testcases are processed correctly

return 0;
}

Download the C++ wrapper class DecFloatClass. To use it, you also have to download the decNumber C Library and pay attention to its licence conditions.

Kontakt:

Richard Kaiser
.img
.img@.img
June 10, 2004