On Dec 28, 12:01 pm, Wes <
wjltemp...@yahoo.com> wrote:
> I noticed that in hpgcc3 when pushing doubles onto the stack as reals,
> some values produce denormalized values. You won't notice the problem
> when the calculator's Number Format is set to STD, but you'll see it
> using FIX, SCI, or ENG.
I've been playing around with the library code and rewrote
__sat3_dbl2real() from scratch (see below). I did a clean rebuild of
everything and all seems to be working correctly. It could probably
be tweaked a little, but it gets the job done and I think the logic is
reasonably easy to follow.
----------------------------------------------------
// Wes Loewer
// 12/29/2011
// log10(2)
#define LOG10OF2 0.30102999566398119521373889
typedef union {
ULONGLONG real; // HP REAL, without prologue
struct {
unsigned exp10 :12; // 3 nibbles
unsigned long long mant10 :48; // 12 nibbles
unsigned sign : 4; // 4 nibbles
};
} hpreal_t;
ULONGLONG __sat3_dbl2real(double x)
{
int carry;
int exp2p1, exp10, i, istop;
double pow10, tenpow10, digit;
hpreal_t r;
unsigned char mant[13]; // one extra digit to allow for rounding
9.999... up to 10.0
r.exp10 = 0; // initialize exponent
r.mant10 = 0; // initialize mantissa
r.sign = x<0 ? 9 : 0; // 9 for negative, 0 for positive or zero
// if (x == 0) then just use the initialized values
if (x != 0.0)
{
x = fabs(x); // use |x| from here on out
frexp(x,&exp2p1); // extract smallest power of 2 > to
x, x!=0
// exp2p1-1 is largest power of 2 <= to x
exp10 = (int)floor((exp2p1-1)*LOG10OF2); // approximate exp10,
could be off by one
pow10 = i10powx(exp10); // largest power of 10 <= to x
// correct any error in exp10 approximation
tenpow10 = 10.0*pow10;
if (tenpow10 <= x)
{
pow10 = tenpow10;
exp10++;
}
else if (pow10 > x) // just to be sure, but I don't think this
is actually possible
{
pow10 /= 10.0;
exp10--;
}
x /= pow10; // x is now the positive significand of the
original x, in the inteval [1,10)
// set exponent in bcd
if (exp10 < 0) // negatives exponents are stored as
1000+exp
exp10 += 1000;
r.exp10 = exp10 % 10; // least significant digit
exp10 /= 10;
r.exp10 |= (exp10 % 10)<<4; // middle digit
exp10 /= 10;
r.exp10 |= (exp10 % 10)<<8; // most significant digit
// set mantissa in bcd, starting with msd in mant[1]
mant[0]=0; // leave mant[0] for possible rounding later
for (i=1; i<13; i++)
{
modf(x,&digit);
mant[i] = (unsigned)digit;
x = (x-digit)*10.0;
}
// check to see if values need rounding
// (this also takes care of repeating 9's)
if (x>=5)
{
carry = 1; // true
i=12;
do
{
mant[i]++; // add the carried 1
if (mant[i] == 10) // is the new digit 10?
mant[i] = 0; // then make it 0 and carry the 1
else
carry = 0; // false, has to eventually get here since
mant[0] is either 0 or 1
i--;
}
while (carry);
}
// start i so as to skip a leading zero
if (mant[0]==0)
{
i = 1;
istop = 12;
}
else
{
i = 0;
istop = 11;
}
// now copy the 12 rounded mantissa digits to r.mant10, starting
with msd
for (; i<=istop; i++)
r.mant10 = (r.mant10 << 4) | mant[i]; // shift 1 nibble and add
next digit
}
return r.real;
}
----------------------------------------------------
-wes