Introduction
I have had this CTextNumber
class lying around for years. I have used it in almost every project I have contributed to. Recently, I found myself updating CTextNumber
, and I decided to share it while I was at it. It is still a really simple implementation, and I'm hoping some code review from others will help to strengthen it.
As a C++ code example, CTextNumber
demonstrates a decent use of operator overloads.
CTextNumber
supports UNICODE and MBCS builds by using TCHAR
types. CTextNumber
depends on the RTL only. The CTextNumber
can be converted to work on CE, with changes to a few lines of code. MFC is only used for the demo application.
Using
After extracting the download, you must build the project and then run its output to see a working sample. Check out the txtnum.h and txtnum.cpp files to examine the CTextNumber
class. Check out the testtextnumDlg.cpp in the testtextnum sub-folder for an example of using the CTextNumber
. You can assign a pointer to the static CTextNumber::s_lpNumberFormat
member to control formatting. The following is a code snippet from the InitDialog()
method in the demo application. The dialog class has m_lbNumbers
defined as an MFC CListBox
.
numberformat = {0};
TCHAR bufILZero[3] = {0};
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILZERO, bufILZero, 3);
numberformat.LeadingZero = _ttoi(bufILZero);
TCHAR bufINegNum[3] = {0};
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, bufINegNum, 3);
numberformat.NegativeOrder = _ttoi(bufINegNum);
numberformat.Grouping = 3;
TCHAR bufSThousands[5] = {0};
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, bufSThousands, 5);
numberformat.lpThousandSep = bufSThousands;
TCHAR bufSDecimal[5] = {0};
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, bufSDecimal, 5);
numberformat.lpDecimalSep = bufSDecimal;
numberformat.NumDigits = 0;
CTextNumber::s_lpNumberFormat = &numberformat;
CString strOut;
CTextNumber tn;
signed __int8 i8 = 0;
tn = i8;
strOut.Format(TEXT("INT8 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i8 = 1;
strOut.Format(TEXT("INT8 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i8 = -1;
strOut.Format(TEXT("INT8 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i8 = SCHAR_MAX;
strOut.Format(TEXT("INT8 value of SCHAR_MAX = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i8 = SCHAR_MIN;
strOut.Format(TEXT("INT8 value of SCHAR_MIN = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
unsigned __int8 ui8 = 0;
tn = ui8;
strOut.Format(TEXT("UINT8 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui8 = 1;
strOut.Format(TEXT("UINT8 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui8 = -1;
strOut.Format(TEXT("UINT8 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui8 = UCHAR_MAX;
strOut.Format(TEXT("UINT8 value of UCHAR_MAX = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
signed __int16 i16 = 0;
tn = i16;
strOut.Format(TEXT("INT16 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i16 = 1;
strOut.Format(TEXT("INT16 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i16 = -1;
strOut.Format(TEXT("INT16 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i16 = SHRT_MAX;
strOut.Format(TEXT("INT16 value of SHRT_MAX = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i16 = SHRT_MIN;
strOut.Format(TEXT("INT16 value of SHRT_MIN = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
unsigned __int16 ui16 = 0;
tn = ui16;
strOut.Format(TEXT("UINT16 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui16 = 1;
strOut.Format(TEXT("UINT16 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui16 = -1;
strOut.Format(TEXT("UINT16 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui16 = USHRT_MAX;
strOut.Format(TEXT("UINT16 value of USHRT_MAX = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
signed __int32 i32 = 0;
tn = i32;
strOut.Format(TEXT("INT32 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i32 = 1;
strOut.Format(TEXT("INT32 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i32 = -1;
strOut.Format(TEXT("INT32 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i32 = LONG_MAX;
strOut.Format(TEXT("INT32 value of LONG_MAX = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i32 = LONG_MIN;
strOut.Format(TEXT("INT32 value of LONG_MIN = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
unsigned __int32 ui32 = 0;
tn = ui32;
strOut.Format(TEXT("UINT32 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui32 = 1;
strOut.Format(TEXT("UINT32 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui32 = -1;
strOut.Format(TEXT("UINT32 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = ui32 = ULONG_MAX;
strOut.Format(TEXT("UINT32 value of ULONG_MAX = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
__int64 i64 = 0;
tn = i64;
strOut.Format(TEXT("INT64 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i64 = 1;
strOut.Format(TEXT("INT64 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i64 = -1;
strOut.Format(TEXT("INT64 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i64 = _I64_MAX;
strOut.Format(TEXT("INT64 value of _I64_MAX = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = i64 = _I64_MIN;
strOut.Format(TEXT("INT64 value of _I64_MIN = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
unsigned __int64 u64 = 0;
tn = u64;
strOut.Format(TEXT("UINT64 value of 0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = u64 = 1;
strOut.Format(TEXT("UINT64 value of 1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = u64 = -1;
strOut.Format(TEXT("UINT64 value of -1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = u64 = _UI64_MAX;
strOut.Format(TEXT("UINT64 value of _UI64_MAX = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
tn = u64 = 9999999999999999999;
strOut.Format(TEXT("UINT64 value of " +
"9999999999999999999 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = u64 = 0x8ac7230489e7ffff;
strOut.Format(TEXT("UINT64 value " +
"of 0x8ac7230489e7ffff = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
numberformat.NumDigits = 1;
float fVal = 0.0f;
tn = fVal;
strOut.Format(TEXT("float value of 0.0 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
tn = fVal = 1.1f;
strOut.Format(TEXT("float value of 1.1 = %s"), tn.GetString());
m_lbNumbers.AddString(strOut);
numberformat.NumDigits = 2;
tn = fVal = 11.11f;
strOut.Format(TEXT("float value of 11.11 = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
numberformat.NumDigits = 3;
tn = fVal = 111.111f;
strOut.Format(TEXT("float value of 111.111 = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
numberformat.NumDigits = 4;
tn = fVal = 1111.1111f;
strOut.Format(TEXT("float value of 1111.1111 = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
numberformat.NumDigits = 5;
tn = fVal = 11111.11111f;
strOut.Format(TEXT("float value of 11111.11111 = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
numberformat.NumDigits = 6;
tn = fVal = 111111.111111f;
strOut.Format(TEXT("float value of 111111.111111 = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
numberformat.NumDigits = 2;
tn = fVal = 3.4E+38f;
strOut.Format(TEXT("float value of 3.4E+38f = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
double dbVal = 0.0;
tn = dbVal = 1.7E+308;
strOut.Format(TEXT("double value of 1.7E+308 = %s"),
tn.GetString());
m_lbNumbers.AddString(strOut);
CString str;
CSize sz;
int dx = 0;
CDC* pDC = m_lbNumbers.GetDC();
for(int i=0; i < m_lbNumbers.GetCount(); i++) {
m_lbNumbers.GetText( i, str );
sz = pDC->GetTextExtent(str);
if(sz.cx > dx)
dx = sz.cx;
}
m_lbNumbers.ReleaseDC(pDC);
m_lbNumbers.SetHorizontalExtent(dx);
(Figure 1. Using the Class)
(Figure 2. Demo Output)
The Class Internals
LPNUMBERFMT CTextNumber::s_lpNumberFormat = NULL;
#define INT8BUFSIZE 10
#define INT16BUFSIZE 20
#define INT32BUFSIZE 20
#define INT64BUFSIZE 40
void CTextNumber::operator=(signed __int8& i8Val) {
TCHAR numBuffer[INT8BUFSIZE+1] = {0};
_itot_s(i8Val, numBuffer, INT8BUFSIZE, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT8BUFSIZE);
}
void CTextNumber::operator=(unsigned __int8& u8Val) {
TCHAR numBuffer[INT8BUFSIZE+1] = {0};
_itot_s(u8Val, numBuffer, INT8BUFSIZE, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT8BUFSIZE);
}
void CTextNumber::operator=(__int16& i16Val) {
TCHAR numBuffer[INT16BUFSIZE+1] = {0};
_itot_s(i16Val, numBuffer, INT16BUFSIZE, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT16BUFSIZE);
}
void CTextNumber::operator=(unsigned __int16& u16Val) {
TCHAR numBuffer[INT16BUFSIZE+1] = {0};
_itot_s(u16Val, numBuffer, INT16BUFSIZE, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT16BUFSIZE);
}
void CTextNumber::operator=(__int32& i32Val) {
TCHAR numBuffer[INT32BUFSIZE+1] = {0};
_itot_s(i32Val, numBuffer, INT32BUFSIZE, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT32BUFSIZE);
}
void CTextNumber::operator=(unsigned __int32& u32Val) {
TCHAR numBuffer[INT32BUFSIZE+1] = {0};
_ultow_s(u32Val, numBuffer, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT32BUFSIZE);
}
void CTextNumber::operator=(__int64& i64Val) {
TCHAR numBuffer[INT64BUFSIZE+1] = {0};
_i64tot_s(i64Val, numBuffer, INT64BUFSIZE, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT64BUFSIZE);
}
void CTextNumber::operator=(unsigned __int64& u64Val) {
TCHAR numBuffer[INT64BUFSIZE+1] = {0};
_ui64tot_s(u64Val, numBuffer, INT64BUFSIZE, 10 );
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, INT64BUFSIZE);
}
void CTextNumber::operator=(float& fVal) {
TCHAR numBuffer[_CVTBUFSIZE+1] = {0};
TCHAR bufFormatter[20] = TEXT("%f");
if(s_lpNumberFormat) {
_stprintf_s(bufFormatter, 20, TEXT("%%.%df"),
s_lpNumberFormat->NumDigits);
}
_stprintf_s(numBuffer, _CVTBUFSIZE, bufFormatter, fVal);
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, _CVTBUFSIZE);
}
void CTextNumber::operator=(double& dbVal) {
TCHAR numBuffer[(_CVTBUFSIZE*2)+1] = {0};
TCHAR bufFormatter[20] = TEXT("%f");
if(s_lpNumberFormat) {
_stprintf_s(bufFormatter, 20, TEXT("%%.%df"),
s_lpNumberFormat->NumDigits);
}
_stprintf_s(numBuffer, (_CVTBUFSIZE*2), bufFormatter, dbVal);
GetNumberFormat(LOCALE_USER_DEFAULT, 0, numBuffer,
s_lpNumberFormat, m_lpcBuffer, (_CVTBUFSIZE*2));
}
(Figure 3. The Class Code)
Alternative
This is a block of code that uses streams to accomplish the same output. Richard Taylor turned me on to this, and I have to say, I prefer it over my class in cases where dependencies on STL are allowed.
#include <locale>
#include <string>
#include <sstream>
#include <iomanip>
using namespace std;
void CtesttextnumDlg::DoSTLMethod() {
wostringstream oss;
locale localeUS( "English_USA" );
oss.imbue( localeUS );
signed __int8 i8 = 0;
oss << _T("INT8 value of 0 = ") << i8;
m_lbNumbers.AddString(oss.str().c_str());
i8 = 1;
oss.str( _T("") );
oss << _T("INT8 value of 1 = ") << i8;
m_lbNumbers.AddString(oss.str().c_str());
i8 = -1;
oss.str( _T("") );
oss << _T("INT8 value of -1 = ") << i8;
m_lbNumbers.AddString(oss.str().c_str());
i8 = SCHAR_MAX;
oss.str( _T("") );
oss << _T("INT8 value of SCHAR_MAX = ") << i8;
m_lbNumbers.AddString(oss.str().c_str());
i8 = SCHAR_MIN;
oss.str( _T("") );
oss << _T("INT8 value of SCHAR_MIN = ") << i8;
m_lbNumbers.AddString(oss.str().c_str());
unsigned __int8 ui8 = 0;
oss.str( _T("") );
oss << _T("UINT8 value of 0 = ") << ui8;
m_lbNumbers.AddString(oss.str().c_str());
ui8 = 1;
oss.str( _T("") );
oss << _T("UINT8 value of 1 = ") << ui8;
m_lbNumbers.AddString(oss.str().c_str());
ui8 = -1;
oss.str( _T("") );
oss << _T("UINT8 value of -1 = ") << ui8;
m_lbNumbers.AddString(oss.str().c_str());
ui8 = UCHAR_MAX;
oss.str( _T("") );
oss << _T("UINT8 value of UCHAR_MAX = ") << ui8;
m_lbNumbers.AddString(oss.str().c_str());
signed __int16 i16 = 0;
oss.str( _T("") );
oss << _T("INT16 value of 0 = ") << i16;
m_lbNumbers.AddString(oss.str().c_str());
i16 = 1;
oss.str( _T("") );
oss << _T("INT16 value of 1 = ") << i16;
m_lbNumbers.AddString(oss.str().c_str());
i16 = -1;
oss.str( _T("") );
oss << _T("INT16 value of -1 = ") << i16;
m_lbNumbers.AddString(oss.str().c_str());
i16 = SHRT_MAX;
oss.str( _T("") );
oss << _T("INT16 value of SHRT_MAX = ") << i16;
m_lbNumbers.AddString(oss.str().c_str());
i16 = SHRT_MIN;
oss.str( _T("") );
oss << _T("INT16 value of SHRT_MIN = ") << i16;
m_lbNumbers.AddString(oss.str().c_str());
unsigned __int16 ui16 = 0;
oss.str( _T("") );
oss << _T("UINT16 value of 0 = ") << ui16;
m_lbNumbers.AddString(oss.str().c_str());
ui16 = 1;
oss.str( _T("") );
oss << _T("UINT16 value of 1 = ") << ui16;
m_lbNumbers.AddString(oss.str().c_str());
ui16 = -1;
oss.str( _T("") );
oss << _T("UINT16 value of -1 = ") << ui16;
m_lbNumbers.AddString(oss.str().c_str());
ui16 = USHRT_MAX;
oss.str( _T("") );
oss << _T("UINT16 value of USHRT_MAX = ") << ui16;
m_lbNumbers.AddString(oss.str().c_str());
signed __int32 i32 = 0;
oss.str( _T("") );
oss << _T("INT32 value of 0 = ") << i32;
m_lbNumbers.AddString(oss.str().c_str());
i32 = 1;
oss.str( _T("") );
oss << _T("INT32 value of 1 = ") << i32;
m_lbNumbers.AddString(oss.str().c_str());
i32 = -1;
oss.str( _T("") );
oss << _T("INT32 value of -1 = ") << i32;
m_lbNumbers.AddString(oss.str().c_str());
i32 = LONG_MAX;
oss.str( _T("") );
oss << _T("INT32 value of LONG_MAX = ") << i32;
m_lbNumbers.AddString(oss.str().c_str());
i32 = LONG_MIN;
oss.str( _T("") );
oss << _T("INT32 value of LONG_MIN = ") << i32;
m_lbNumbers.AddString(oss.str().c_str());
unsigned __int32 ui32 = 0;
oss.str( _T("") );
oss << _T("UINT32 value of 0 = ") << ui32;
m_lbNumbers.AddString(oss.str().c_str());
ui32 = 1;
oss.str( _T("") );
oss << _T("UINT32 value of 1 = ") << ui32;
m_lbNumbers.AddString(oss.str().c_str());
ui32 = -1;
oss.str( _T("") );
oss << _T("UINT32 value of -1 = ") << ui32;
m_lbNumbers.AddString(oss.str().c_str());
ui32 = ULONG_MAX;
oss.str( _T("") );
oss << _T("UINT32 value of ULONG_MAX = ") << ui32;
m_lbNumbers.AddString(oss.str().c_str());
__int64 i64 = 0;
oss.str( _T("") );
oss << _T("INT64 value of 0 = ") << i64;
m_lbNumbers.AddString(oss.str().c_str());
i64 = 1;
oss.str( _T("") );
oss << _T("INT64 value of 1 = ") << i64;
m_lbNumbers.AddString(oss.str().c_str());
i64 = -1;
oss.str( _T("") );
oss << _T("INT64 value of -1 = ") << i64;
m_lbNumbers.AddString(oss.str().c_str());
i64 = _I64_MAX;
oss.str( _T("") );
oss << _T("INT64 value of _I64_MAX = ") << i64;
m_lbNumbers.AddString(oss.str().c_str());
i64 = _I64_MIN;
oss.str( _T("") );
oss << _T("INT64 value of _I64_MIN = ") << i64;
m_lbNumbers.AddString(oss.str().c_str());
unsigned __int64 u64 = 0;
oss.str( _T("") );
oss << _T("UINT64 value of 0 = ") << u64;
m_lbNumbers.AddString(oss.str().c_str());
u64 = 1;
oss.str( _T("") );
oss << _T("UINT64 value of 1 = ") << u64;
m_lbNumbers.AddString(oss.str().c_str());
u64 = -1;
oss.str( _T("") );
oss << _T("UINT64 value of -1 = ") << u64;
m_lbNumbers.AddString(oss.str().c_str());
u64 = _UI64_MAX;
oss.str( _T("") );
oss << _T("UINT64 value of _UI64_MAX = ") << u64;
m_lbNumbers.AddString(oss.str().c_str());
u64 = 9999999999999999999;
oss.str( _T("") );
oss << _T("UINT64 value of 9999999999999999999 = ") << u64;
m_lbNumbers.AddString(oss.str().c_str());
u64 = 0x8ac7230489e7ffff;
oss.str( _T("") );
oss << _T("UINT64 value of 0x8ac7230489e7ffff = ") << u64;
m_lbNumbers.AddString(oss.str().c_str());
float fVal = 0.0f;
oss.str( _T("") );
oss << _T("float value of 0.0f = ")
<< std::setprecision(2) << fVal;
m_lbNumbers.AddString(oss.str().c_str());
fVal = 1.1f;
oss.str( _T("") );
oss << _T("float value of 1.1f = ") << fVal;
fVal = 11.11f;
oss.str( _T("") );
oss << _T("float value of 11.11f = ")
<< std::fixed << std::setprecision(2)
<< std::showpoint << fVal;
m_lbNumbers.AddString(oss.str().c_str());
fVal = 111.111f;
oss.str( _T("") );
oss << _T("float value of 111.111f = ")
<< std::fixed << std::setprecision(3)
<< std::showpoint << fVal;
m_lbNumbers.AddString(oss.str().c_str());
fVal = 1111.1111f;
oss.str( _T("") );
oss << _T("float value of 1111.1111f = ")
<< std::fixed << std::setprecision(4)
<< std::showpoint << fVal;
m_lbNumbers.AddString(oss.str().c_str());
fVal = 11111.11111f;
oss.str( _T("") );
oss << _T("float value of 11111.11111f = ")
<< std::fixed << std::setprecision(5)
<< std::showpoint << fVal;
m_lbNumbers.AddString(oss.str().c_str());
fVal = 111111.111111f;
oss.str( _T("") );
oss << _T("float value of 111111.111111f = ")
<< std::fixed << std::setprecision(6)
<< std::showpoint << fVal;
m_lbNumbers.AddString(oss.str().c_str());
fVal = 3.4E+38f;
oss.str( _T("") );
oss << _T("float value of 3.4E+38f = ")
<< std::fixed << std::setprecision(2)
<< std::showpoint << fVal;
m_lbNumbers.AddString(oss.str().c_str());
double dbVal = 0.0;
dbVal = 1.7E+308;
oss.str( _T("") );
oss << _T("double value of 1.7E+308 = ")
<< std::fixed << std::setprecision(2)
<< std::showpoint << dbVal;
m_lbNumbers.AddString(oss.str().c_str());
}
(Figure 3. STL Demo Output)
Summary
If I left out any details you think should be mentioned in the article, please let me know.
If you could take one last second to rate this article or even leave a comment, it would be much appreciated.
Thanks for reading!
History
- August 9, 2006: Fixed bug that caused the bogus conversion of a
double
with a max value. - July 27, 2006: Using
GetNumberFormat
now; Added support for float
s and double
s. - July 27, 2006: There was a bug with the
__int32
conversion, and no support for __int16
in the first post ... both are fixed now.