[Nickle] nickle: Branch 'master'
Keith Packard
keithp at keithp.com
Sat Dec 7 22:30:58 PST 2024
float.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 63 insertions(+), 9 deletions(-)
New commits:
commit 80dbe382144791b3f13853d3633646bff9e80be9
Author: Keith Packard <keithp at keithp.com>
Date: Sat Dec 7 22:30:04 2024 -0800
float: Round even in FloatDivide and NewFloat
Make sure we perform correct rounding whenever dropping precision
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/float.c b/float.c
index 2c1bb51..9752578 100644
--- a/float.c
+++ b/float.c
@@ -153,19 +153,25 @@ FpartMult (Fpart *a, Fpart *b)
}
static Fpart *
-FpartDivide (Fpart *a, Fpart *b)
+FpartDivide (Fpart *a, Fpart *b, Natural **remp)
{
ENTER ();
- Natural *rem;
- Natural *quo;
- Sign sign;
+ Natural *quo;
+ Sign sign;
+ Fpart *fquo;
+ Natural *rem;
sign = Positive;
if (a->sign != b->sign)
sign = Negative;
quo = NaturalDivide (a->mag, b->mag, &rem);
- RETURN (NewFpart (sign, quo));
+ fquo = NewFpart (sign, quo);
+ EXIT();
+ REFERENCE(fquo);
+ REFERENCE(rem);
+ *remp = rem;
+ return fquo;
}
static Fpart *
@@ -182,6 +188,38 @@ FpartLsl (Fpart *a, int shift)
RETURN (NewFpart (a->sign, NaturalLsl (a->mag, shift)));
}
+static int
+FpartRound (Fpart *a, int shift)
+{
+
+ if (!shift)
+ return 0;
+
+ Natural *n = a->mag;
+ int l = n->length;
+ int round = 0;
+ digit *d = NaturalDigits(n);
+
+ while (l && shift > DIGITBITS) {
+ if (*d++ != 0)
+ round = 1;
+ l--;
+ shift -= DIGITBITS;
+ }
+
+ if (l) {
+ digit last = *d;
+
+ shift -= 2;
+ if (shift < 0) {
+ last <<= 1;
+ shift = 0;
+ }
+ round |= (last >> shift) & 3;
+ }
+ return round;
+}
+
static Bool
FpartLess (Fpart *a, Fpart *b)
{
@@ -405,6 +443,7 @@ FloatDivide (Value av, Value bv, int expandOk)
Fpart *exp;
unsigned prec;
unsigned iprec, alen;
+ Natural *rem;
(void) expandOk;
if (FpartZero (b->mant))
@@ -419,9 +458,10 @@ FloatDivide (Value av, Value bv, int expandOk)
prec = a->prec;
else
prec = b->prec;
- iprec = prec + FpartLength (bmant) + 1;
- alen = FpartLength (amant);
exp = b->exp;
+ /* 2 guard bits */
+ iprec = prec + FpartLength (bmant) + 2;
+ alen = FpartLength (amant);
if (alen < iprec)
{
amant = FpartLsl (amant, iprec - alen);
@@ -430,7 +470,10 @@ FloatDivide (Value av, Value bv, int expandOk)
exp = FpartAdd (a->exp, exp, True);
DebugFp ("amant ", amant);
DebugFp ("bmant ", bmant);
- mant = FpartDivide (amant, bmant);
+ mant = FpartDivide (amant, bmant, &rem);
+ /* set the low guard bit if there was a remainder */
+ if (!NaturalZero(rem))
+ NaturalDigits(mant->mag)[0] |= 1;
DebugFp ("mant ", mant);
DebugFp ("exp ", exp);
RETURN (NewFloat (mant, exp, prec));
@@ -1113,6 +1156,7 @@ NewFloat (Fpart *mant, Fpart *exp, unsigned prec)
ENTER ();
unsigned bits, dist;
Value ret;
+ int round;
DebugFp ("New mant", mant);
DebugFp ("New exp ", exp);
@@ -1124,7 +1168,17 @@ NewFloat (Fpart *mant, Fpart *exp, unsigned prec)
{
dist = bits - prec;
exp = FpartAdd (exp, NewIntFpart (dist), False);
+ round = FpartRound (mant, dist);
mant = FpartRsl (mant, dist);
+ if (round & 2) {
+ if ((round & 1) || !NaturalEven(mant->mag)) {
+ mant->mag = NaturalPlus(mant->mag, one_natural);
+ if (FpartLength(mant) != prec) {
+ exp = FpartAdd(exp, one_fpart, False);
+ mant = FpartRsl(mant, 1);
+ }
+ }
+ }
}
/*
* Canonicalize by shifting to a 1 in the LSB
@@ -1186,7 +1240,7 @@ NewRationalFloat (Rational *r, unsigned prec)
}
#define SCALE_BITS 52
-#define SCALE 4503599627370496.0 /* 2 ** 52 */
+#define SCALE 0x1p52 /* 2 ** 52 */
Value
NewDoubleFloat (double d)
More information about the Nickle
mailing list