~ 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