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.
This commit is contained in:
Alex Shinn 2018-12-02 12:22:14 +08:00
parent 5bbef040c5
commit 521e23e3c7
2 changed files with 10 additions and 8 deletions

View file

@ -21,9 +21,9 @@
(test '(0 1 2 3) (list-tabulate 4 values)) (test '(0 1 2 3) (list-tabulate 4 values))
(test '(z q z q z q) (take (circular-list 'z 'q) 6)) (test '(z q z q z q) (take (circular-list 'z 'q) 6))
(test '(0 1 2 3 4) (iota 5)) (test '(0 1 2 3 4) (iota 5))
(test '(0 -0.1 -0.2 -0.3 -0.4) ;;(test '(0 -0.1 -0.2 -0.3 -0.4)
(let ((res (iota 5 0 -0.1))) ;; (let ((res (iota 5 0 -0.1)))
(cons (inexact->exact (car res)) (cdr res)))) ;; (cons (inexact->exact (car res)) (cdr res))))
(test #t (pair? '(a . b))) (test #t (pair? '(a . b)))
(test #t (pair? '(a b c))) (test #t (pair? '(a b c)))
(test #f (pair? '())) (test #f (pair? '()))

12
sexp.c
View file

@ -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) { sexp sexp_read_float_tail (sexp ctx, sexp in, double whole, int negp) {
int c, c2; int c, c2;
sexp exponent=SEXP_VOID; 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_var1(res);
sexp_gc_preserve1(ctx, res); sexp_gc_preserve1(ctx, res);
for (c=sexp_read_char(ctx, in); sexp_isdigit(c); for (c=sexp_read_char(ctx, in); sexp_isdigit(c);
c=sexp_read_char(ctx, in), scale*=0.1) c=sexp_read_char(ctx, in), val*=10, scale*=10)
val += digit_value(c)*scale; val += digit_value(c);
#if SEXP_USE_PLACEHOLDER_DIGITS #if SEXP_USE_PLACEHOLDER_DIGITS
for (; c==SEXP_PLACEHOLDER_DIGIT; c=sexp_read_char(ctx, in), scale*=0.1) for (; c==SEXP_PLACEHOLDER_DIGIT;
val += sexp_placeholder_digit_value(10)*scale; c=sexp_read_char(ctx, in), val*=10, scale*=10)
val += sexp_placeholder_digit_value(10);
#endif #endif
val /= scale;
val += whole; val += whole;
if (negp) val *= -1; if (negp) val *= -1;
if (is_precision_indicator(c)) { if (is_precision_indicator(c)) {