(float / double)

A

abdul_n_khan

Hello,
I have a basic question related to datatypes.
I am trying to read a value using Microsoft's ADO recordset from a
field (lets call it 'Price') with datatype decimal(19,6) => 19 =
Precision, 6 = Scale


1) When I read this field into float datatype. I get a value 1.9000,
which is correct. But when I read its value in a double datatype I get
1.8999999761581.


2) My second question is related to multiplication.
Should it cause any problem?

dblPrice =1.23333;
Amount = 91000000;
PricedAmount= dblPrice * Amount
PricedAmountshould have 112233030

But i am getting 112230300 (thus I am loosing precision).

Here is the implementation of Multiplication operator of CCurrency
class



CCurrency CCurrency::eek:perator*(float nOperand) const
{
if (!GetStatus() == valid)
return *this;
CCurrency curOperand;
VarCyFromR4(nOperand,&curOperand.m_cur);
CCurrency curResult;
HRESULT hr = VarCyMul(*this, curOperand,&curResult.m_cur);
if FAILED(hr)
{
curResult.SetStatus(invalid);
curResult.m_cur.int64 = 0;
}
return curResult;
}

CCurrency CCurrency::eek:perator*(double nOperand) const
{
if (!GetStatus() == valid)
return *this;
CCurrency curOperand;
VarCyFromR8(nOperand,&curOperand.m_cur);
CCurrency curResult;
HRESULT hr = VarCyMul(*this, curOperand,&curResult.m_cur);
if FAILED(hr)
{
curResult.SetStatus(invalid);
curResult.m_cur.int64 = 0;
}
return curResult;
}
 
B

Bob Hairgrove

Hello,
I have a basic question related to datatypes.
I am trying to read a value using Microsoft's ADO recordset from a
field (lets call it 'Price') with datatype decimal(19,6) => 19 =
Precision, 6 = Scale


1) When I read this field into float datatype. I get a value 1.9000,
which is correct. But when I read its value in a double datatype I get
1.8999999761581.

DECIMAL is a fixed-point numeric type, and such numbers often cannot
be expressed exactly as a floating-point number in a computer. For
example, 1.5 can be stored exactly with no loss of precision because
0.5 is equivalent to 1/2 which is 0.1 in binary notation. Similarly,
any other number which can be expressed as the sum of various powers
of 2 can be represented exactly. But not 1/10, for example, and not
9/10.

The float value *appears* to be exactly the same as the number in the
database. In reality, the ODBC driver has rounded the value for you.
The double has more precision, so you see the effects of the rounding
errors between decimal and floating-point types.
2) My second question is related to multiplication.
Should it cause any problem?

dblPrice =1.23333;
Amount = 91000000;
PricedAmount= dblPrice * Amount
PricedAmountshould have 112233030

But i am getting 112230300 (thus I am loosing precision).
Obviously.

Here is the implementation of Multiplication operator of CCurrency
class



CCurrency CCurrency::eek:perator*(float nOperand) const
{
if (!GetStatus() == valid)
return *this;
CCurrency curOperand;
VarCyFromR4(nOperand,&curOperand.m_cur);
CCurrency curResult;
HRESULT hr = VarCyMul(*this, curOperand,&curResult.m_cur);
if FAILED(hr)
{
curResult.SetStatus(invalid);
curResult.m_cur.int64 = 0;
}
return curResult;
}

CCurrency CCurrency::eek:perator*(double nOperand) const
{
if (!GetStatus() == valid)
return *this;
CCurrency curOperand;
VarCyFromR8(nOperand,&curOperand.m_cur);
CCurrency curResult;
HRESULT hr = VarCyMul(*this, curOperand,&curResult.m_cur);
if FAILED(hr)
{
curResult.SetStatus(invalid);
curResult.m_cur.int64 = 0;
}
return curResult;
}

My suggestion would be to implement CCurrency as a decimal scaled
integer value (i.e., use long long if you have it, else __int64) and
not as a double. That way, you avoid the rounding problems.

But how many digits will you need? Do any of the values in the
database exceed INT64_MAX scaled to a precision of 6? If so, you'll
need to either look for a third-party solution or implement one
yourself.
 
V

Victor Bazarov

I have a basic question related to datatypes.
I am trying to read a value using Microsoft's ADO recordset from a
field (lets call it 'Price') with datatype decimal(19,6) => 19 =
Precision, 6 = Scale


1) When I read this field into float datatype. I get a value 1.9000,
which is correct. But when I read its value in a double datatype I get
1.8999999761581.

Do you know how those numbers (19 and 6) relate to the significant
digits and the range of the number? Since MS ADO "recordset" is not
defined in C++, you have to tell us. Then together we can think why
you see what you see.
2) My second question is related to multiplication.
Should it cause any problem?

dblPrice =1.23333;
Amount = 91000000;
PricedAmount= dblPrice * Amount
PricedAmountshould have 112233030

But i am getting 112230300 (thus I am loosing precision).

"Losing". Well, how are 'dblPrice' and 'Amount' declared? Hos is
'PricedAmount' declared?
Here is the implementation of Multiplication operator of CCurrency
class
[..]

How is this relevant to your questions?

If you need an industrial-strength system for handling money, you
should _not_ use 'float' or even 'double'. You should implement
your own numeric representation based most likely on fixed-point
arithmetic with something like 5 digits after the decimal point.

Look on the web for 'fixed-point arithmetic' and you shall find.

V
 
A

abdul_n_khan

Amount & PricedAmount are objects of CCurrency class (implementation is
part of my own project).

dblPrice is declared as double.

Implementation of CCurrency is as follows
---
class CCurrency
{
// Constructors
public:
CCurrency();
CCurrency(CURRENCY cySrc);
CCurrency(const CCurrency& curSrc);
CCurrency(const _variant_t & varSrc);
CCurrency(long nUnits, long nFractionalUnits);
CCurrency(__int64 int64Value);
// Attributes
public:
enum CCurrencyStatus
{
valid = 0,
invalid = 1, // Invalid currency (overflow, div 0, etc.)
null = 2, // Literally has no value
};
CURRENCY m_cur;
CCurrencyStatus m_status;
void SetStatus(CCurrencyStatus status);
CCurrencyStatus GetStatus() const;
// Operations
public:

const CCurrency& operator=(CURRENCY cySrc);
const CCurrency& operator=(const CCurrency& curSrc);
const CCurrency& operator=(const _variant_t & varSrc);

BOOL operator==(const CCurrency& cur) const;
BOOL operator!=(const CCurrency& cur) const;
BOOL operator<(const CCurrency& cur) const;
BOOL operator>(const CCurrency& cur) const;
BOOL operator<=(const CCurrency& cur) const;
BOOL operator>=(const CCurrency& cur) const;

// CCurrency math
CCurrency operator+(const CCurrency& cur) const;
CCurrency operator-(const CCurrency& cur) const;
const CCurrency& operator+=(const CCurrency& cur);
const CCurrency& operator-=(const CCurrency& cur);
CCurrency operator-() const;

CCurrency operator*(const CCurrency& cur) const;

CCurrency operator*(long nOperand) const;
CCurrency operator*(float nOperand) const;
CCurrency operator*(double nOperand) const;
CCurrency operator/(long nOperand) const;
CCurrency operator/(float nOperand) const;
CCurrency operator/(double nOperand) const;

const CCurrency& operator*=(long nOperand);
const CCurrency& operator/=(long nOperand);
const CCurrency& operator*=(float nOperand);
const CCurrency& operator/=(float nOperand);
const CCurrency& operator*=(double nOperand);
const CCurrency& operator/=(double nOperand);

operator CURRENCY() const;
operator _variant_t() const;

// CCurrency definition
void SetCurrency(long nUnits, long nFractionalUnits);
CCurrency Absolute();
};
---


Victor said:
I have a basic question related to datatypes.
I am trying to read a value using Microsoft's ADO recordset from a
field (lets call it 'Price') with datatype decimal(19,6) => 19 =
Precision, 6 = Scale


1) When I read this field into float datatype. I get a value 1.9000,
which is correct. But when I read its value in a double datatype I get
1.8999999761581.

Do you know how those numbers (19 and 6) relate to the significant
digits and the range of the number? Since MS ADO "recordset" is not
defined in C++, you have to tell us. Then together we can think why
you see what you see.
2) My second question is related to multiplication.
Should it cause any problem?

dblPrice =1.23333;
Amount = 91000000;
PricedAmount= dblPrice * Amount
PricedAmountshould have 112233030

But i am getting 112230300 (thus I am loosing precision).

"Losing". Well, how are 'dblPrice' and 'Amount' declared? Hos is
'PricedAmount' declared?
Here is the implementation of Multiplication operator of CCurrency
class
[..]

How is this relevant to your questions?

If you need an industrial-strength system for handling money, you
should _not_ use 'float' or even 'double'. You should implement
your own numeric representation based most likely on fixed-point
arithmetic with something like 5 digits after the decimal point.

Look on the web for 'fixed-point arithmetic' and you shall find.

V
 
A

abdul_n_khan

Hello,
I am working on a program written by someone else and facing problems
in precisions due to usage of floating point date types. My input is
are the values in the rows of database (MS SQL Server) where fixed
point datatypes are used to store input as well as result e.g.
decimal(16,6) & money.

As the end I end up only using precision of 4 decimal instead of 6.

dblPrice =1.23333;
Amount = 91000000;
PricedAmount= dblPrice * Amount
PricedAmountshould have 112233030

But i am getting 112230300 (thus I am loosing precision).

dblPrice is declared as double
PricedAmount and Amount are objects of CCurrency

Here is the implementation of Multiplication operator of CCurrency
class and declaration of CCurrency

Any help to make this program more robust for more real implementation.

I have started to consider writign a class for fixed point arithmatic
datatype. Please let me know if it is a good start.

thanks and best wishes

abdul n. khan

---
class CCurrency
{
// Constructors
public:
CCurrency();
CCurrency(CURRENCY cySrc);
CCurrency(const CCurrency& curSrc);
CCurrency(const _variant_t & varSrc);
CCurrency(long nUnits, long nFractionalUnits);
CCurrency(__int64 int64Value);
// Attributes
public:
enum CCurrencyStatus
{
valid = 0,
invalid = 1, // Invalid currency (overflow, div 0,
etc.)
null = 2, // Literally has no value
};
CURRENCY m_cur;
CCurrencyStatus m_status;
void SetStatus(CCurrencyStatus status);
CCurrencyStatus GetStatus() const;
// Operations
public:

const CCurrency& operator=(CURRENCY cySrc);
const CCurrency& operator=(const CCurrency& curSrc);
const CCurrency& operator=(const _variant_t & varSrc);

BOOL operator==(const CCurrency& cur) const;
BOOL operator!=(const CCurrency& cur) const;
BOOL operator<(const CCurrency& cur) const;
BOOL operator>(const CCurrency& cur) const;
BOOL operator<=(const CCurrency& cur) const;
BOOL operator>=(const CCurrency& cur) const;

// CCurrency math
CCurrency operator+(const CCurrency& cur) const;
CCurrency operator-(const CCurrency& cur) const;
const CCurrency& operator+=(const CCurrency& cur);
const CCurrency& operator-=(const CCurrency& cur);
CCurrency operator-() const;

CCurrency operator*(const CCurrency& cur) const;

CCurrency operator*(long nOperand) const;
CCurrency operator*(float nOperand) const;
CCurrency operator*(double nOperand) const;
CCurrency operator/(long nOperand) const;
CCurrency operator/(float nOperand) const;
CCurrency operator/(double nOperand) const;

const CCurrency& operator*=(long nOperand);
const CCurrency& operator/=(long nOperand);
const CCurrency& operator*=(float nOperand);
const CCurrency& operator/=(float nOperand);
const CCurrency& operator*=(double nOperand);
const CCurrency& operator/=(double nOperand);

operator CURRENCY() const;
operator _variant_t() const;

// CCurrency definition
void SetCurrency(long nUnits, long nFractionalUnits);
CCurrency Absolute();
};

CCurrency CCurrency::eek:perator*(float nOperand) const
{
if (!GetStatus() == valid)
return *this;
CCurrency curOperand;
VarCyFromR4(nOperand,&curOperand.m_cur);
CCurrency curResult;
HRESULT hr = VarCyMul(*this, curOperand,&curResult.m_cur);
if FAILED(hr)
{
curResult.SetStatus(invalid);
curResult.m_cur.int64 = 0;
}
return curResult;

}

CCurrency CCurrency::eek:perator*(double nOperand) const
{
if (!GetStatus() == valid)
return *this;
CCurrency curOperand;
VarCyFromR8(nOperand,&curOperand.m_cur);
CCurrency curResult;
HRESULT hr = VarCyMul(*this, curOperand,&curResult.m_cur);
if FAILED(hr)
{
curResult.SetStatus(invalid);
curResult.m_cur.int64 = 0;
}
return curResult;
}
 
B

Ben Pope

Hello,
I am working on a program written by someone else and facing problems
in precisions due to usage of floating point date types. My input is
are the values in the rows of database (MS SQL Server) where fixed
point datatypes are used to store input as well as result e.g.
decimal(16,6) & money.

As the end I end up only using precision of 4 decimal instead of 6.

So far two people have responded and they both, independently told you
no to use floating point to represent currency. Dare I say, don't use
floating point to store currency?
Any help to make this program more robust for more real implementation.

Well, ignoring the responses you don't like won't solve it.
I have started to consider writign a class for fixed point arithmatic
datatype. Please let me know if it is a good start.

That's specifically what you have been told to do. At least twice.

Ben Pope
 
A

abdul_n_khan

1) database datatype "money" is being handled by class CCurrency, which
uses structure CURRENCY that is defined by
typedef struct tagCY
{
unsigned long Lo;
long Hi;
} CURRENCY;
Members
Lo : Low-order 32 bits of the currency value.
Hi : High-order 32 bits of the currency value.
Remarks : The CURRENCY structure is a scaled integer representation of
a decimal number with four digits to the right of the decimal point. A
stored value of 327500, for example, is to be construed as representing
a currency value of 32.7500.

Therefore in my understanding "database datatype money" is being
handled by fixed point datatype instead of floating point. Please
correct me if I am wrong here.

The only problem is handling of "database datatype decimal(16,6)". I
know that I (or someone who originally wrote the program) shouldn't
have used troubling floating point datatype (single precision / double
precision).

But what alternate solution should I be doing? In my understanding that
is a very typical and generic problem that is taken care by many
people. I will appreciate any help to some article or documentation or
example will be of great help.

Does C++ or MS Visual C++ support "decimal" datatype?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top