I have a COleCurrency
object that represents a unit price. And I have a double
value that represents a quantity. And I need to calculate the total dollar amount to the nearest penny.
Looks like COleCurrency
has built in multiplication operators, but only for multiplication with a long
value.
I can multiply COleCurrency.m_cur.int64
by the double
, but that converts the double
to __int64
so it wouldn't be accurate.
What is the best way to accurately multiply a COleCurrency
by a double
?
CodePudding user response:
Finite binary floating point values form a proper subset of finite decimal values. While any given floating point value has an exact, finite representation in decimal, the opposite isn't true. In other words, not every decimal can be represented using a finite binary floating point value. A simple example is 0.1
that produces an infinite sequence of binary digits when converted to a binary floating point value.
The important point here is that if you are dealing with fractional values, using binary floating point values to represent them will in general introduce inaccuracies (with very few exceptions, such as 0.5
). The only way to perform accurate multiplications with an integer value is to use an integer as the multiplicand.
Since you have opted to use a floating point value the only thing you can do is limit the inaccuracies. The proposed solution:
__int64 x = currency.m_cur.int64 * (__int64)dbl;
suffers from the same "possible loss of data" issue the compiler warned about. Since you're now using an explicit cast, this silences the compiler. The effect is still the same: The floating point value gets truncated.
A better approach would be to convert the 64-bit integer value to a double
first. This produces an exact floating point representation of the integer value, provided that it is within range (roughly /- 1e15). You can then multiply with another floating point value (which is subject to rounding errors), and finally round the result using, e.g., std::llround
:
__int64 x = std::llround(static_cast<double>(currency.m_cur.int64) * dbl);