~ 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