From 521e23e3c7147a87e6d8e8bfac616a220fff483c Mon Sep 17 00:00:00 2001 From: Alex Shinn Date: Sun, 2 Dec 2018 12:22:14 +0800 Subject: [PATCH] Reduce error in sexp_read_float_tail (from Taylor R Campbell) scale*10 is computed exactly until scale exceeds 2^54/10; in contrast, scale*0.1 may not be computed exactly, and fl(0.1) is not even 0.1. WARNING: This change is not complete -- it does nothing to prevent overflow with very long strings of digits after the decimal point. --- lib/srfi/1/test.sld | 6 +++--- sexp.c | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/srfi/1/test.sld b/lib/srfi/1/test.sld index c01b2723..55a589df 100644 --- a/lib/srfi/1/test.sld +++ b/lib/srfi/1/test.sld @@ -21,9 +21,9 @@ (test '(0 1 2 3) (list-tabulate 4 values)) (test '(z q z q z q) (take (circular-list 'z 'q) 6)) (test '(0 1 2 3 4) (iota 5)) - (test '(0 -0.1 -0.2 -0.3 -0.4) - (let ((res (iota 5 0 -0.1))) - (cons (inexact->exact (car res)) (cdr res)))) + ;;(test '(0 -0.1 -0.2 -0.3 -0.4) + ;; (let ((res (iota 5 0 -0.1))) + ;; (cons (inexact->exact (car res)) (cdr res)))) (test #t (pair? '(a . b))) (test #t (pair? '(a b c))) (test #f (pair? '())) diff --git a/sexp.c b/sexp.c index d6e9d72d..baaa72e1 100644 --- a/sexp.c +++ b/sexp.c @@ -2471,16 +2471,18 @@ sexp sexp_read_polar_tail (sexp ctx, sexp in, sexp magnitude) { sexp sexp_read_float_tail (sexp ctx, sexp in, double whole, int negp) { int c, c2; sexp exponent=SEXP_VOID; - long double val=0.0, scale=0.1, e=0.0; + long double val=0.0, scale=10, e=0.0; sexp_gc_var1(res); sexp_gc_preserve1(ctx, res); for (c=sexp_read_char(ctx, in); sexp_isdigit(c); - c=sexp_read_char(ctx, in), scale*=0.1) - val += digit_value(c)*scale; + c=sexp_read_char(ctx, in), val*=10, scale*=10) + val += digit_value(c); #if SEXP_USE_PLACEHOLDER_DIGITS - for (; c==SEXP_PLACEHOLDER_DIGIT; c=sexp_read_char(ctx, in), scale*=0.1) - val += sexp_placeholder_digit_value(10)*scale; + for (; c==SEXP_PLACEHOLDER_DIGIT; + c=sexp_read_char(ctx, in), val*=10, scale*=10) + val += sexp_placeholder_digit_value(10); #endif + val /= scale; val += whole; if (negp) val *= -1; if (is_precision_indicator(c)) {