C++ Decimal Wrapper Class

I’ve been searching round the internet for a C# compatible C++ class for handling Decimal data types to no avail.  I discovered a neat and fast IEEE 754 implementation in C from Intel, but alas … no C++ implementation. IEEE 754 (Decimal Floating Point) has been suggested to be integrated in the upcoming C++0x standard, but untill compilers will support it, we’d be stuck in C++ with float and double values :(.

I have looked at other implementations including decNumber C++ wrapper and Marc Clifton’s article on his Decimal class, but they don’t suit my needs. I need to handle conversion from C# decimal to C++, values of C# decimal are serialized and sent via TCP/IP and deserialized with the C++ application on the server side. The class needs to convert from a C# Decimal to a C++ Decimal type.

To those who are unfamiliar with IEEE 754 or Decimal floating point, please note that C and C++ float and double have trouble storing a floating point numbers – it stores them in base-2, and computers have trouble representing them in binary format. Take for example the following :


double n = 0.3;
n -= 0.099;
n *= 1000;

if (n == 201) printf("true\n");
else printf("false\n");

As per the example above, you may have guessed that the final value of n is 201, but it’s not. In reality, double and float has trouble storing the value of 1/3 – which .333333… and repeats to infinity. Although doing operations in binary as opposed to BCD/Decimal is much faster, it sacrifices accuracy. This has a huge impact on Financial and high-precision applications where accuracy of a price or value is paramount.

C# provides a Decimal data type which has the accuracy of storing numbers with a precision of 28-29 significant digits. It does this by storing allocating a 128 bit Decimal value, storing a significand in a 96-bit unsigned int, a byte for the exponent (-127 + 128), an a flag byte for the sign (most significant bit), the rest of the bits are unused. So if you are to store 12345.6789m in C#’s decimal, its significand (0-95th bit) will contain the value of 123456789, the exponent byte will contain -4 (this determines the posiiton of the decimal point), and the flag bit which is zero.

C#’s internal representation isn’t compatible with IEEE 754′s implementation, in fact the IEEE 754 implementation is much more precise than C#’s. In Intel’s BID implementation, the significand is stored in a 113 bit number, the exponent is 17 bits wide, and a sign flag bit (128 bit total). This is where I needed a class to convert from/to C#’s decimal number.

Before one can compile the code below, my implementation relies on Intel’s Decimal Floating Point Math library (http://software.intel.com/en-us/articles/intel-decimal-floating-point-math-library/). You can link the C++ implementation of the class to the Intel’s built libraries (using pass by value). I’ll explain more on this later.

The class is generally a C++ wrapper class to most of Intel’s DFP functions.

#ifndef __DECIMAL_H
#define __DECIMAL_H

#ifdef WIN32
#define LX "%I64x"
#else
#ifdef HPUX_OS
#define LX "%llx"
#else
#define LX "%Lx"
#endif
#endif

#if BID_BIG_ENDIAN
#define HIGH_128W 0
#define LOW_128W 1
#else
#define HIGH_128W 1
#define LOW_128W 0
#endif

/* basic decimal floating-point types */

#if defined _MSC_VER
#if defined _M_IX86 && !defined __INTEL_COMPILER // Win IA-32, MS compiler
#define ALIGN(n)
#else
#define ALIGN(n) __declspec(align(n))
#endif
#else
#define ALIGN(n) __attribute__ ((aligned(n)))
#endif

extern "C"
{
#include "IntelDFP/bid_conf.h"
#include "IntelDFP/bid_functions.h"
#include "IntelDFP/bid_internal.h"
}

#include
#include

typedef unsigned int Decimal32;
typedef unsigned long long Decimal64;
typedef ALIGN(16) struct { unsigned long long w[2]; } Decimal128;

/* rounding modes */
/* Values for _IDEC_round */
typedef enum _IDEC_roundingmode {
_IDEC_nearesteven = 0,
_IDEC_downward = 1,
_IDEC_upward = 2,
_IDEC_towardzero = 3,
_IDEC_nearestaway = 4,
_IDEC_dflround = _IDEC_nearesteven
} _IDEC_roundingmode;

/* exception flags */
typedef enum _IDEC_flagbits {
_IDEC_invalid = 0x01,
_IDEC_zerodivide = 0x04,
_IDEC_overflow = 0x08,
_IDEC_underflow = 0x10,
_IDEC_inexact = 0x20,
_IDEC_allflagsclear = 0x00
} _IDEC_flagbits;

// Just wrap the data bytes in a CS decimal
// The logic of this class can be combined in Decimal
// if you want to. However, that would mean sizeof(Decimal)
// to exceed 16 bytes (128 bit which is the size of C#'s decimal), as
// Decimal class below has some other private member values.
// So I end up declaring this wrapper class/type instead.
struct CSDecimal{
CSDecimal();

unsigned char value[16];

unsigned int hi();
unsigned int mid();
unsigned int lo();
unsigned int flags();

// Set the value of the 96 bit significand
void setcoeff(unsigned int l, unsigned int m, unsigned int h);

int sign();
// Set the sign bit (value[0]'s most significnat bit)
void setsign(int sign);
unsigned int scale();
// Set the scale
void setscale(unsigned int scale);
};

// The actual Decimal class. A wrapper to the BID functions
// for IEEE 754 implementation by Intel. This calls library
// functions. Under gcc, this calls the built-in libs.
// The underlying type is a BID_UINT128
class Decimal{
public:
Decimal();
virtual ~Decimal(void);

// From a string
Decimal(std::string str);
// From floating types
Decimal(const Decimal& d);
Decimal(const float v); // Construct from float
Decimal(const double v); // Construct from double
// From integers
Decimal(const unsigned int v); // Int32
Decimal(const unsigned long long v); // Uint64
Decimal(const int v);
Decimal(const long long v);
// From a CSharp Decimal
Decimal(CSDecimal &); // Construct form CSharp decimal bytes

// Assignments
template
Decimal & operator=(const T &val)
{
Decimal tmpVal(val);
n = tmpVal.n;
return *this;
}
// Converters
std::string ToString(void);
float ToFloat(void) const;
double ToDouble(void) const;
int ToInt32(bool isFloor=false) const;
unsigned int ToUInt32(bool isFloor=false) const;
long long ToInt64(bool isFloor=false) const;
unsigned long long ToUInt64(bool isFloor=false) const;
// To CSDecimal
CSDecimal ToBits(void);

Decimal operator+(const Decimal&);
Decimal operator-(const Decimal&);
Decimal operator*(const Decimal&);
Decimal operator/(const Decimal&);

Decimal & operator+=(const Decimal &);
Decimal & operator-=(const Decimal &);
Decimal & operator*=(const Decimal &);
Decimal & operator/=(const Decimal &);

Decimal & operator++(); // prefix
Decimal operator++(int); // postfix
Decimal & operator--(); // prefix
Decimal operator--(int); //postfix

#if 1
// TODO:
Decimal operator%(const Decimal &);
#endif

bool operator==(const Decimal&) const;
bool operator!=(const Decimal&) const;
bool operator<(const Decimal&) const;
bool operator(const Decimal&) const;
bool operator>=(const Decimal&) const;

void UnPack(Decimal32 &lo, Decimal32 &mid, Decimal32 &hi, int &exp, int &sign);
void Pack(Decimal32 lo, Decimal32 mid, Decimal32 hi, int exp, int sign);

friend std::ostream & operator<<(std::ostream &os, Decimal &d);

protected:
/* Internal representation of our Decimal class */
//_Quad n;
BID_UINT128 n;
/* Internal representation is a 128 bit floating point
* number. Using IEEE 754 standard for floating point
* arithmetic. This also utilizes the co-processor for
* doing computations of this data type. This is much
* more precise than C#'s 96 bit mantissa.
*/
};
#endif

To use the class, consider the following code snippit below:


Decimal myDecimal("123.456");
myDecimal += 4.5; // Add a float value to the decimal
cout << myDecimal << endl;

I’ll discuss more on the usage specially on converting from C# to c++ on my next post.

More to this later ….

Links to the code:

http://ofwlife.files.wordpress.com/2009/07/decimal-cpp.doc

http://ofwlife.files.wordpress.com/2009/07/decimal-h.doc

Reove the .doc extensions.

About these ads

~ by vpcola on July 5, 2009.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: