From 493e9dd8c6f49a4738e4434152728b4a465f9ec4 Mon Sep 17 00:00:00 2001 From: Peter Bex Date: Mon, 16 Oct 2017 20:35:33 +0200 Subject: [PATCH 2/2] 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. --- chicken.h | 14 -------------- runtime.c | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) 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..2997b1a5 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, m; C_word *a, /* self = av[ 0 ] */ k = av[ 1 ], @@ -10450,7 +10450,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(C_modf(f, &m) == 0.0 && f >= 0 && log2(f) < C_WORD_SIZE) { /* Use fast int code */ if(f < 0) { p = to_n_nary((C_uword)-f, radix, 1, 1); } else { -- 2.11.0