~ chicken-core (chicken-5) a72d3d5958b51d5abdbee9b959a54d49663a58b5


commit a72d3d5958b51d5abdbee9b959a54d49663a58b5
Author:     Peter Bex <peter@more-magic.net>
AuthorDate: Mon Oct 16 20:35:33 2017 +0200
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Thu Oct 26 20:14:13 2017 +0200

    Fix flonum to string conversion on extreme edge cases on 64-bit archs
    
    This bug was exposed by the previous commit which restored the tests
    to work as intended, because the serialization of (expt 2.0 64)
    resulted in "0.0" in compiled code, causing the exact/inexact
    comparison tests between the bignum (expt 2 64) and the flonum
    (expt 2.0 64.0) to fail.
    
    This representation fails because C_fits_in_unsigned_int_p will
    incorrectly return C_SCHEME_TRUE on a 64-bit architecture due to how
    we perform the casting: Because UINT_MAX is 2^64-1, it will be
    normalized by the floating-point representation to the nearest
    floating-point value that can represent it: 2^64.
    Thus, (double)2^64 <= UINT_MAX is true.  When we then cast the
    double to an unsigned word, it will result in zero due to overflow.
    
    The fix here is to simply check if the log2 of the number is less than
    the number of bits in an UWORD, rather than trying a direct value
    comparison.  As a bonus, this allows us to finally get rid of the
    obsolete C_fits_in_unsigned_int_p() inline helper function.
    
    Signed-off-by: Peter Bex <peter@more-magic.net>

diff --git a/chicken.h b/chicken.h
index 9e2af974..45571d45 100644
--- a/chicken.h
+++ b/chicken.h
@@ -2420,20 +2420,6 @@ inline static C_word C_i_bignump(C_word x)
 }
 
 
-
-/* XXX TODO OBSOLETE (but still used by C_flonum_to_string) */
-inline static C_word C_fits_in_unsigned_int_p(C_word x)
-{
-  double n, m;
-
-  if(x & C_FIXNUM_BIT) return C_SCHEME_TRUE;
-  if(C_truep(C_i_bignump(x))) return C_mk_bool(C_bignum_size(x) == 1);
-
-  n = C_flonum_magnitude(x);
-  return C_mk_bool(C_modf(n, &m) == 0.0 && n >= 0 && n <= C_UWORD_MAX);
-}
-
-
 inline static double C_c_double(C_word x)
 {
   if(x & C_FIXNUM_BIT) return (double)C_unfix(x);
diff --git a/runtime.c b/runtime.c
index e1970cd6..9423993a 100644
--- a/runtime.c
+++ b/runtime.c
@@ -10432,7 +10432,7 @@ void C_ccall C_fixnum_to_string(C_word c, C_word *av)
 void C_ccall C_flonum_to_string(C_word c, C_word *av)
 {
   C_char *p;
-  double f;
+  double f, fa, m;
   C_word *a,
     /* self = av[ 0 ] */
     k = av[ 1 ],
@@ -10440,6 +10440,7 @@ void C_ccall C_flonum_to_string(C_word c, C_word *av)
     radix = ((c == 3) ? 10 : C_unfix(av[ 3 ]));
 
   f = C_flonum_magnitude(num);
+  fa = fabs(f);
 
   /* XXX TODO: Should inexacts be printable in other bases than 10?
    * Perhaps output a string starting with #i?
@@ -10450,7 +10451,7 @@ void C_ccall C_flonum_to_string(C_word c, C_word *av)
     barf(C_BAD_ARGUMENT_TYPE_BAD_BASE_ERROR, "number->string", C_fix(radix));
   }
 
-  if(C_fits_in_unsigned_int_p(num) == C_SCHEME_TRUE) { /* Use fast int code */
+  if(f == 0.0 || (C_modf(f, &m) == 0.0 && log2(fa) < C_WORD_SIZE)) { /* Use fast int code */
     if(f < 0) {
       p = to_n_nary((C_uword)-f, radix, 1, 1);
     } else {
Trap