I am using Embarcadero C++ Builder XE10 for building an application that does some monetary calculations.
While trying to use the System::Currency datatype I am facing a couple of problems.
Q1: Why does the calculation of the modulus fail when using the "%" operator?
System::Currency TaxValue = 1.665;
System::Currency Rest = 0;
// Correct result: Rest will be 0.005 when converted to double
Rest = TaxValue - ( TaxValue / 100 * 100 );
// Incorrect result: Rest is the same as TaxValue
Rest = TaxValue % 100;
Edit: I have been totally fooled by the debugger's output where the System::Currency's value is shown in its integer represention multiplied by 10.000.
What I really expected to see was:
Rest = (TaxValue * 10000) % 100;
==> Now Rest is 50, which is exactly what I expected.
Q2: How can I do a correct banker's rounding with the Curreny data type?
Examples:
1.664 => 1.66
1.665 => 1.67
1.666 => 1.67
Herwig
Q1: Why does the calculation of the modulus fail when using the "%" operator?
System::Currency
operates with a precision of 4 decimal places. Your example expects 2-digit precision instead.
System::Currency
maintains its precision without rounding errors by internally multiplying input values by 10000
and then using integer math instead of floating-point math to manipulate values.
When you initialize TaxValue
with 1.665
, its internal Val
member (which is an __int64
) is set to (1.665 * 10000) = 16650
. Here is what that constructor looks like:
__fastcall Currency(double val) {Val = _roundToInt64(10000 * val);}
When you then perform TaxValue % 100
, the %
operator is implemented like this:
Currency __fastcall operator %(int rhs) const
{return Currency(static_cast<int>(Val % (10000 * (__int64)rhs))) / 10000;}
The first part creates a temp Currency
object that is initialized with an int
value of (16650 % (10000 * 100)) = 16650
, which gets multiplied by 10000
to 166500000
by the constructor of the temp object:
__fastcall Currency(int val) {Val = 10000*(__int64)val;}
The second part then divides the temp by 10000
. The /
operator is implemented like this:
Currency& __fastcall operator /=(const Currency& rhs)
{Val *= 10000; Val /= rhs.Val; return *this;}
Currency __fastcall operator /(int rhs) const
{Currency tmp(*this); return tmp /= Currency(rhs);}
Thus producing a final Currency
object whose Val
has been set to (166500000 * 10000) / (10000 * 10000) = 16650
.
When that final Currency
is then assigned to Rest
and converted to double
, the value is divided by 10000
, thus producing 1.665
:
__fastcall operator double() const {return ((double)Val) / 10000;}
Q2: How can I do a correct banker's rounding with the Curreny data type?
Have a look at the System::Round()
function, which uses banker's rounding.
If you want more control over the rounding, use the System::Math::RoundTo()
function, or find a 3rd party rounding function.
There are several other questions on StackOverflow that deal with Currency
rounding, for instance:
How to get Delphi Currency Type to Round like Excel all the time?
(System::Currency
is C++Builder's wrapper for Delphi's native Currency
type).