$ 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