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 here. To use it, you also have to
download the decNumber
C Library and pay attention to its licence conditions.
Mail to Richard Kaiser
www.rkaiser.de
June 10, 2004