At present, in my Decimal Plugin, the Decimal type can be converted to
the Double type, and vice versa. So when a Decimal variable is equated
to a Currency variable, RB converts the Currency variable to a Double
and then my plugin converts the Double to a Decimal. The Double type
also intermediates when a Currency variable is set equal to a Decimal
variable. This may be "good enough", but it is not a perfect solution.
It's not perfect because the Currency variable is really an Int64 with
an assumed decimal point giving 4 places to the right of the decimal
point. The largest positive number that fits into an Int64 is 2^63 - 1
= 9,223,372,036,854,775,807 which has 19 significant figures, so the
largest currency value is 922,337,203,685,477.5807. But the largest
mantissa (ignoring decimal point and exponent) in an 8 byte double is
2^53 = 9,007,199,254,740,992. That has 16 significant figures, so it
lacks about 3 significant figures from the highest currency value. So
not all currency values fit into a double.
So in my Decimal Plugin, with Xcode, I created a function which took
an Int64 (as a long long) and converted that to a Decimal with 4
digits after the decimal point. I'm using GMP for the multi-precision
library. Here's the simple source:
// get dec from currency (treated as INT64 x 10^-4)
void getDec_Curr(DecData& z, long long& x)
{
bool isNegative;
long long xx;
xx = x;
if(xx<0)
{
isNegative = true;
xx = -xx;
}
else
isNegative = false;
z.i = (unsigned long int)xx;
z.e = -4;
if(isNegative)
z.i = -z.i;
normalize(z);
}/* getDec_Curr */
Note that I call the Int64 by reference. I also tried calling by value
with the same results given below.
In DecimalPlugin.cpp I have the function:
static REALobject convertCurrToDec(long long& x)
{
REALobject z;
DecData *zData;
z = REALnewInstance("Decimal");
zData = (DecData *) REALGetClassData(z, &DecClass);
getDec_Curr(*zData, x);
return z;
}/* convertCurrToDec */
and the other methods definition:
{ (REALproc) convertCurrToDec, REALnoImplementation,
"DecimalFromCurrency(ByRef x As Currency) As Decimal" }
The revised plugin built with no errors. A simple RB program for
testing has a PushButton with source:
Dim z As Decimal
Dim c As Currency // holds 15 digits to left of decimal point and 4
digits to the right
DecSetScale(4)
c = CDbl(EditFieldx.text)
z = DecimalFromCurrency(c)
EditFieldz.text = str(z)
One finds the results:
Input c = 1.2345
Output x = 1.2345
c = -1.2345
x = -1.2345
c = 12345.6789
x = 12345.6789
c = 123456.7891
x = 123456.7891
c = 1234567.8912
x = 375574.4320 problem!
After a lot of experimenting, I found the solution to the problem. RB
is incorrectly passing only the lower 32 bits of a 64 bit currency
number to the plugin. To verify this, one inputs the currency number
2^32/10^4 = 429496.7296:
c = 429496.7296
x = 0.0000
Then adding anything below 2^32/10^4 to 429496.7296 should give that
added number in x:
c = 429496.7296 + 123456.7891 = 552953.5187
x = 123456.7891
And 2*2^32/10^4 should again give a zero for x:
c = 858993.4592
x = 0.0000
and so on.
Then I changed Currency to Int64 in the definition:
{ (REALproc) convertCurrToDec, REALnoImplementation,
"DecimalFromCurrency(ByRef x As Int64) As Decimal" }
and used the RB program:
Dim z As Decimal
Dim c As Int64
DecSetScale(4)
c = CDbl(EditFieldx.text)
z = DecimalFromCurrency(c)
EditFieldz.text = str(z)
so now:
c = 12345 (no decimal point!)
x = 1.2345 (I didn't change convertCurrToDec so the decimal point is ok)
and so on. But for 2^32 input:
c = 4294967296
x = 0.0000
So the same problem exists for an Int64 value. RB incorrectly passes
only the lower 32 bits of the Int64 value to a plugin. I haven't
tested a UInt 64, but my guess is that value won't be passed correctly
either.
I think this is a bug in RB 2007 R5.
Bob
_______________________________________________
Unsubscribe or switch delivery mode:
<http://www.realsoftware.com/support/listmanager/>
Search the archives:
<http://support.realsoftware.com/listarchives/lists.html>