~ chicken-core (chicken-5) 18ce467a28f4f180b5efebdaeb167498c1b19508


commit 18ce467a28f4f180b5efebdaeb167498c1b19508
Author:     Peter Bex <peter.bex@xs4all.nl>
AuthorDate: Sun Sep 22 16:30:56 2013 +0200
Commit:     Christian Kellermann <ckeen@pestilenz.org>
CommitDate: Wed Oct 2 20:20:39 2013 +0200

    Fix #1051: use C99 isnormal() and return canned values.
    
    Instead of relying on unusable code from numbers egg, we return
    +1.0/+inf.0 or -1.0/+inf.0 when given a subnormal floating-point number.
    
    Signed-off-by: Christian Kellermann <ckeen@pestilenz.org>

diff --git a/runtime.c b/runtime.c
index 4ad05445..9bef9400 100644
--- a/runtime.c
+++ b/runtime.c
@@ -7371,46 +7371,27 @@ void C_ccall C_flonum_fraction(C_word c, C_word closure, C_word k, C_word n)
 
 void C_ccall C_flonum_rat(C_word c, C_word closure, C_word k, C_word n)
 {
-  double frac, tmp, numer, denom, factor, fn = C_flonum_magnitude(n);
-  double r1a, r1b;
+  double frac, tmp, numer, denom, fn = C_flonum_magnitude(n);
   double ga, gb;
   C_word ab[WORDS_PER_FLONUM * 2], *ap = ab;
   int i = 0;
 
-  if (n < 1 && n > -1) {
-    factor = pow(2, DBL_MANT_DIG);
-    fn *= factor;
-  } else {
-    factor = 1;
-  }
-
-  /* Calculate bit-length of the fractional part (ie, after decimal point) */
-  frac = fn;
-  while(!C_isnan(frac) && !C_isinf(frac) && C_modf(frac, &tmp) != 0.0) {
-    frac *= 2;
-    if (i++ > 3000) /* should this be flonum-maximum-exponent? */
-      barf(C_CANT_REPRESENT_INEXACT_ERROR, "fprat", n);
-  }
-
-  /* r1a and r1b are integral and form the rational number r1 = r1a/r1b. */
-  r1b = pow(2, i);
-  r1a = fn*r1b;
-
-  /*
-   * We "multiply" r1 with r2 given that r2 = 1/factor.
-   * result = (r1a * (factor / g)) / abs(r1b / g)   | g = gcd(r1b, factor)
-   */
-  ga = r1b;
-  gb = factor;
-  while(gb != 0.0) {
-    tmp = fmod(ga, gb);
-    ga = gb;
-    gb = tmp;
+  if (isnormal(fn)) {
+    /* Calculate bit-length of the fractional part (ie, after decimal point) */
+    frac = fn;
+    while(!C_isnan(frac) && !C_isinf(frac) && C_modf(frac, &tmp) != 0.0) {
+      frac *= 2;
+      if (i++ > 3000) /* should this be flonum-maximum-exponent? */
+        barf(C_CANT_REPRESENT_INEXACT_ERROR, "fprat", n);
+    }
+    
+    /* Now we can compute the rational number r = 2^i/X*2^i = numer/denom. */
+    denom = pow(2, i);
+    numer = fn*denom;
+  } else { /* denormalised/subnormal number: [+-]1.0/+inf.0 */
+    numer = fn > 0.0 ? 1.0 : -1.0;
+    denom = 1.0/0.0; /* +inf */
   }
-  /* ga now holds gcd(r1b, factor), and r1b and ga are absolute already */
-  numer = r1a * (factor / ga);
-  denom = r1b / ga;
-
   C_values(4, C_SCHEME_UNDEFINED, k, C_flonum(&ap, numer), C_flonum(&ap, denom));
 }
 
diff --git a/tests/library-tests.scm b/tests/library-tests.scm
index aaef6fd1..711341b4 100644
--- a/tests/library-tests.scm
+++ b/tests/library-tests.scm
@@ -86,6 +86,15 @@
 (assert (equal? 5.0 (numerator 1.25)))
 (assert (equal? 4.0 (denominator 1.25)))
 (assert (equal? -5.0 (numerator -1.25)))
+
+;; A few denormalised numbers, cribbed from NetBSD ATF tests for ldexp():
+(assert (equal? 1.0 (numerator 1.1125369292536006915451e-308)))
+(assert (equal? +inf.0 (denominator 1.1125369292536006915451e-308)))
+(assert (equal? -1.0 (numerator -5.5626846462680034577256e-309)))
+(assert (equal? +inf.0 (denominator -5.5626846462680034577256e-309)))
+(assert (equal? 1.0 (numerator 4.9406564584124654417657e-324)))
+(assert (equal? +inf.0 (denominator 4.9406564584124654417657e-324)))
+
 (assert (equal? 4.0 (denominator -1.25)))
 (assert (equal? 1e10 (numerator 1e10)))
 (assert (equal? 1.0 (denominator 1e10)))
Trap