$ csi -R numbers
...
#;1> (= 0.1 (/ 1 10))
#f
#;2> (= 0.1 (exact->inexact (/ 1 10)))
#f
#;3> (exact->inexact (/ 1 10))
0.1

Note that both equalities hold when not using the numbers
egg.  Exact = comparisons on floating point numbers are
rare, but this is just a symptom of a more general issue
which was triggering a 1 in 10,000 bug in the new set of fmt
formatting tests.

The problem is that numbers uses the function mpq_get_d for
converting from a rational to a double, but the description
of that function is:

   Function: double mpq_get_d (mpq_t op)

   Convert op to a double, truncating if necessary
       (ie. rounding towards zero).

So it deliberately loses precision by truncating instead of
rounding.  There is no function to do the same with rounding
in GMP.

As a potential workaround, I've attached a patch which first
checks if both the numerator and denominator fit within a
long (there's no direct way to check if they fit in a
double), and if so just uses the straight C operations

    (double) numerator / (double) denominator

which is more accurate than what GMP does!

If they don't fit we fall back on mpq_get_d, but in that
case we're going to lose precision whether we want to or not
so it's not a big deal.

The attached patch implements this, and passes the above
test, the numbers egg tests, and the fmt egg tests, but I'd
prefer someone more familiar with GMP look at it before
checking it in.

-- 
Alex

Index: numbers-c.c
===================================================================
--- numbers-c.c (revision 9881)
+++ numbers-c.c (working copy)
@@ -55,10 +55,23 @@
 }
 
 
+static double
+mpq_get_d_accurately(mpq_t rat)
+{
+  mpz_t *num, *den;
+  num = mpq_numref(rat);
+  den = mpq_denref(rat);
+  if (mpz_fits_slong_p(*num) && mpz_fits_slong_p(*den)) {
+    return (double) (mpz_get_d(*num) / mpz_get_d(*den));
+  } else {
+    return mpq_get_d(rat);
+  }
+}
+
 static C_word
 rat_to_flo(C_word **p, C_word n, C_word rat)
 {
-  return C_flonum(p, mpq_get_d(rat_of(rat)));
+  return C_flonum(p, mpq_get_d_accurately(rat_of(rat)));
 }
 
 
@@ -1189,7 +1202,7 @@
       return C_mk_bool(C_flonum_magnitude(x) == C_flonum_magnitude(y));
 
     case C_fix(RAT):
-      return C_mk_bool(C_flonum_magnitude(x) == mpq_get_d(rat_of(y)));
+      return C_mk_bool(C_flonum_magnitude(x) == 
mpq_get_d_accurately(rat_of(y)));
 
     default: return C_SCHEME_FALSE;
     }
@@ -1210,7 +1223,7 @@
   case C_fix(RAT):
     switch(ty) {
     case C_fix(FLO):
-      return C_mk_bool(mpq_get_d(rx) == C_flonum_magnitude(y));
+      return C_mk_bool(mpq_get_d_accurately(rx) == C_flonum_magnitude(y));
 
     case C_fix(RAT):
       { defrat(ry, y);
_______________________________________________
Chicken-users mailing list
Chicken-users@nongnu.org
http://lists.nongnu.org/mailman/listinfo/chicken-users

Reply via email to