[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Guile-commits] GNU Guile branch, stable-2.0, updated. v2.0.9-26-g95ed22
From: |
Mark H Weaver |
Subject: |
[Guile-commits] GNU Guile branch, stable-2.0, updated. v2.0.9-26-g95ed221 |
Date: |
Tue, 16 Jul 2013 04:32:25 +0000 |
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Guile".
http://git.savannah.gnu.org/cgit/guile.git/commit/?id=95ed221785f5b1203e998823455f682c1830498b
The branch, stable-2.0 has been updated
via 95ed221785f5b1203e998823455f682c1830498b (commit)
via ba0e46ea1b56ff6164daa9d5fe0778029ca3beee (commit)
via 01329288918de3ab4b7d85d4c0c5b83b0edfc179 (commit)
via 4cc2e41cf78bccf13d7dfc44f74b7c11d13dbf33 (commit)
from 7e8166f5bdb526c021c826943aaf050134cccc83 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 95ed221785f5b1203e998823455f682c1830498b
Author: Mark H Weaver <address@hidden>
Date: Tue Jul 16 00:26:11 2013 -0400
Avoid lossy conversion from inum to double in numerical comparisons.
* libguile/numbers.c (scm_less_p): Avoid converting inums to doubles.
* test-suite/tests/numbers.test (<): Add tests.
commit ba0e46ea1b56ff6164daa9d5fe0778029ca3beee
Author: Mark H Weaver <address@hidden>
Date: Tue Jul 16 00:22:10 2013 -0400
numbers.test: Avoid inexact arithmetic in computation of fixnum-bit.
* test-suite/tests/numbers.test (fixnum-bit): Rewrite to avoid
inexact arithmetic.
commit 01329288918de3ab4b7d85d4c0c5b83b0edfc179
Author: Mark H Weaver <address@hidden>
Date: Tue Jul 16 00:18:40 2013 -0400
Fix bugs in numerical equality predicate.
* libguile/numbers.c (scm_num_eq_p): Fix bug comparing fractions to
infinities (reported by GÃ¶ran Weinholt <address@hidden>). Fix
erroneous comment describing the logic behind inum/flonum comparison.
Use similar logic for inum/complex comparison to avoid rounding
errors. Make minor indentation fixes and simplifications.
* test-suite/tests/numbers.test (=): Add tests.
commit 4cc2e41cf78bccf13d7dfc44f74b7c11d13dbf33
Author: Mark H Weaver <address@hidden>
Date: Tue Jul 16 00:00:23 2013 -0400
Fix rounding in scm_i_divide2double for negative arguments.
* libguile/numbers.c (INUM_LOSSLESSLY_CONVERTIBLE_TO_DOUBLE):
New macro.
(scm_i_divide2double): Use INUM_LOSSLESSLY_CONVERTIBLE_TO_DOUBLE to
determine if our fast path is safe. Previously, negative arguments
were not checked properly.
* test-suite/tests/numbers.test (exact->inexact): Add tests.
-----------------------------------------------------------------------
Summary of changes:
libguile/numbers.c | 115 +++++++++++++++++++++++++++++------------
test-suite/tests/numbers.test | 83 +++++++++++++++++++++++++++++-
2 files changed, 163 insertions(+), 35 deletions(-)
diff --git a/libguile/numbers.c b/libguile/numbers.c
index 1f4b9a8..d09b7c5 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -100,6 +100,13 @@ typedef scm_t_signed_bits scm_t_inum;
#define DOUBLE_IS_POSITIVE_INFINITY(x) (isinf(x) && ((x) > 0))
#define DOUBLE_IS_NEGATIVE_INFINITY(x) (isinf(x) && ((x) < 0))
+/* Test an inum to see if it can be converted to a double without loss
+ of precision. Note that this will sometimes return 0 even when 1
+ could have been returned, e.g. for large powers of 2. It is designed
+ to be a fast check to optimize common cases. */
+#define INUM_LOSSLESSLY_CONVERTIBLE_TO_DOUBLE(n) \
+ (SCM_I_FIXNUM_BIT-1 <= DBL_MANT_DIG \
+ || ((n) ^ ((n) >> (SCM_I_FIXNUM_BIT-1))) < (1L << DBL_MANT_DIG))
#if ! HAVE_DECL_MPZ_INITS
@@ -506,10 +513,10 @@ scm_i_divide2double (SCM n, SCM d)
if (SCM_LIKELY (SCM_I_INUMP (d)))
{
- if (SCM_LIKELY (SCM_I_INUMP (n)
- && (SCM_I_FIXNUM_BIT-1 <= DBL_MANT_DIG
- || (SCM_I_INUM (n) < (1L << DBL_MANT_DIG)
- && SCM_I_INUM (d) < (1L << DBL_MANT_DIG)))))
+ if (SCM_LIKELY
+ (SCM_I_INUMP (n)
+ && INUM_LOSSLESSLY_CONVERTIBLE_TO_DOUBLE (SCM_I_INUM (n))
+ && INUM_LOSSLESSLY_CONVERTIBLE_TO_DOUBLE (SCM_I_INUM (d))))
/* If both N and D can be losslessly converted to doubles, then
we can rely on IEEE floating point to do proper rounding much
faster than we can. */
@@ -6535,9 +6542,11 @@ scm_num_eq_p (SCM x, SCM y)
to a double and compare.
But on a 64-bit system an inum is bigger than a double and
- casting it to a double (call that dxx) will round. dxx is at
- worst 1 bigger or smaller than xx, so if dxx==yy we know yy is
- an integer and fits a long. So we cast yy to a long and
+ casting it to a double (call that dxx) will round.
+ Although dxx will not in general be equal to xx, dxx will
+ always be an integer and within a factor of 2 of xx, so if
+ dxx==yy, we know that yy is an integer and fits in
+ scm_t_signed_bits. So we cast yy to scm_t_signed_bits and
compare with plain xx.
An alternative (for any size system actually) would be to check
@@ -6552,8 +6561,14 @@ scm_num_eq_p (SCM x, SCM y)
|| xx == (scm_t_signed_bits) yy));
}
else if (SCM_COMPLEXP (y))
- return scm_from_bool (((double) xx == SCM_COMPLEX_REAL (y))
- && (0.0 == SCM_COMPLEX_IMAG (y)));
+ {
+ /* see comments with inum/real above */
+ double ry = SCM_COMPLEX_REAL (y);
+ return scm_from_bool ((double) xx == ry
+ && 0.0 == SCM_COMPLEX_IMAG (y)
+ && (DBL_MANT_DIG >= SCM_I_FIXNUM_BIT-1
+ || xx == (scm_t_signed_bits) ry));
+ }
else if (SCM_FRACTIONP (y))
return SCM_BOOL_F;
else
@@ -6608,24 +6623,21 @@ scm_num_eq_p (SCM x, SCM y)
else if (SCM_BIGP (y))
{
int cmp;
- if (isnan (SCM_REAL_VALUE (x)))
+ if (isnan (xx))
return SCM_BOOL_F;
- cmp = xmpz_cmp_d (SCM_I_BIG_MPZ (y), SCM_REAL_VALUE (x));
+ cmp = xmpz_cmp_d (SCM_I_BIG_MPZ (y), xx);
scm_remember_upto_here_1 (y);
return scm_from_bool (0 == cmp);
}
else if (SCM_REALP (y))
- return scm_from_bool (SCM_REAL_VALUE (x) == SCM_REAL_VALUE (y));
+ return scm_from_bool (xx == SCM_REAL_VALUE (y));
else if (SCM_COMPLEXP (y))
- return scm_from_bool ((SCM_REAL_VALUE (x) == SCM_COMPLEX_REAL (y))
- && (0.0 == SCM_COMPLEX_IMAG (y)));
+ return scm_from_bool ((xx == SCM_COMPLEX_REAL (y))
+ && (0.0 == SCM_COMPLEX_IMAG (y)));
else if (SCM_FRACTIONP (y))
{
- double xx = SCM_REAL_VALUE (x);
- if (isnan (xx))
+ if (isnan (xx) || isinf (xx))
return SCM_BOOL_F;
- if (isinf (xx))
- return scm_from_bool (xx < 0.0);
x = scm_inexact_to_exact (x); /* with x as frac or int */
goto again;
}
@@ -6635,8 +6647,15 @@ scm_num_eq_p (SCM x, SCM y)
else if (SCM_COMPLEXP (x))
{
if (SCM_I_INUMP (y))
- return scm_from_bool ((SCM_COMPLEX_REAL (x) == (double) SCM_I_INUM (y))
- && (SCM_COMPLEX_IMAG (x) == 0.0));
+ {
+ /* see comments with inum/real above */
+ double rx = SCM_COMPLEX_REAL (x);
+ scm_t_signed_bits yy = SCM_I_INUM (y);
+ return scm_from_bool (rx == (double) yy
+ && 0.0 == SCM_COMPLEX_IMAG (x)
+ && (DBL_MANT_DIG >= SCM_I_FIXNUM_BIT-1
+ || (scm_t_signed_bits) rx == yy));
+ }
else if (SCM_BIGP (y))
{
int cmp;
@@ -6650,20 +6669,18 @@ scm_num_eq_p (SCM x, SCM y)
}
else if (SCM_REALP (y))
return scm_from_bool ((SCM_COMPLEX_REAL (x) == SCM_REAL_VALUE (y))
- && (SCM_COMPLEX_IMAG (x) == 0.0));
+ && (SCM_COMPLEX_IMAG (x) == 0.0));
else if (SCM_COMPLEXP (y))
return scm_from_bool ((SCM_COMPLEX_REAL (x) == SCM_COMPLEX_REAL (y))
- && (SCM_COMPLEX_IMAG (x) == SCM_COMPLEX_IMAG (y)));
+ && (SCM_COMPLEX_IMAG (x) == SCM_COMPLEX_IMAG
(y)));
else if (SCM_FRACTIONP (y))
{
double xx;
if (SCM_COMPLEX_IMAG (x) != 0.0)
return SCM_BOOL_F;
xx = SCM_COMPLEX_REAL (x);
- if (isnan (xx))
+ if (isnan (xx) || isinf (xx))
return SCM_BOOL_F;
- if (isinf (xx))
- return scm_from_bool (xx < 0.0);
x = scm_inexact_to_exact (x); /* with x as frac or int */
goto again;
}
@@ -6679,10 +6696,8 @@ scm_num_eq_p (SCM x, SCM y)
else if (SCM_REALP (y))
{
double yy = SCM_REAL_VALUE (y);
- if (isnan (yy))
+ if (isnan (yy) || isinf (yy))
return SCM_BOOL_F;
- if (isinf (yy))
- return scm_from_bool (0.0 < yy);
y = scm_inexact_to_exact (y); /* with y as frac or int */
goto again;
}
@@ -6692,10 +6707,8 @@ scm_num_eq_p (SCM x, SCM y)
if (SCM_COMPLEX_IMAG (y) != 0.0)
return SCM_BOOL_F;
yy = SCM_COMPLEX_REAL (y);
- if (isnan (yy))
+ if (isnan (yy) || isinf(yy))
return SCM_BOOL_F;
- if (isinf (yy))
- return scm_from_bool (0.0 < yy);
y = scm_inexact_to_exact (y); /* with y as frac or int */
goto again;
}
@@ -6754,7 +6767,25 @@ scm_less_p (SCM x, SCM y)
return scm_from_bool (sgn > 0);
}
else if (SCM_REALP (y))
- return scm_from_bool ((double) xx < SCM_REAL_VALUE (y));
+ {
+ /* We can safely take the ceiling of y without changing the
+ result of x<y, given that x is an integer. */
+ double yy = ceil (SCM_REAL_VALUE (y));
+
+ /* In the following comparisons, it's important that the right
+ hand side always be a power of 2, so that it can be
+ losslessly converted to a double even on 64-bit
+ machines. */
+ if (yy >= (double) (SCM_MOST_POSITIVE_FIXNUM+1))
+ return SCM_BOOL_T;
+ else if (!(yy > (double) SCM_MOST_NEGATIVE_FIXNUM))
+ /* The condition above is carefully written to include the
+ case where yy==NaN. */
+ return SCM_BOOL_F;
+ else
+ /* yy is a finite integer that fits in an inum. */
+ return scm_from_bool (xx < (scm_t_inum) yy);
+ }
else if (SCM_FRACTIONP (y))
{
/* "x < a/b" becomes "x*b < a" */
@@ -6797,7 +6828,25 @@ scm_less_p (SCM x, SCM y)
else if (SCM_REALP (x))
{
if (SCM_I_INUMP (y))
- return scm_from_bool (SCM_REAL_VALUE (x) < (double) SCM_I_INUM (y));
+ {
+ /* We can safely take the floor of x without changing the
+ result of x<y, given that y is an integer. */
+ double xx = floor (SCM_REAL_VALUE (x));
+
+ /* In the following comparisons, it's important that the right
+ hand side always be a power of 2, so that it can be
+ losslessly converted to a double even on 64-bit
+ machines. */
+ if (xx < (double) SCM_MOST_NEGATIVE_FIXNUM)
+ return SCM_BOOL_T;
+ else if (!(xx < (double) (SCM_MOST_POSITIVE_FIXNUM+1)))
+ /* The condition above is carefully written to include the
+ case where xx==NaN. */
+ return SCM_BOOL_F;
+ else
+ /* xx is a finite integer that fits in an inum. */
+ return scm_from_bool ((scm_t_inum) xx < SCM_I_INUM (y));
+ }
else if (SCM_BIGP (y))
{
int cmp;
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index eca4536..5e95ab9 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -33,7 +33,10 @@
(not (not (object-documentation object))))
(define fixnum-bit
- (inexact->exact (+ (/ (log (+ most-positive-fixnum 1)) (log 2)) 1)))
+ (do ((i 0 (+ 1 i))
+ (n 1 (* 2 n)))
+ ((> n most-positive-fixnum)
+ (+ 1 i))))
(define fixnum-min most-negative-fixnum)
(define fixnum-max most-positive-fixnum)
@@ -2034,7 +2037,28 @@
(pass-if (not (= (ash-flo 1.0 58) (1- (ash 1 58)))))
(pass-if (= (ash 1 58) (ash-flo 1.0 58)))
(pass-if (not (= (1+ (ash 1 58)) (ash-flo 1.0 58))))
- (pass-if (not (= (1- (ash 1 58)) (ash-flo 1.0 58)))))
+ (pass-if (not (= (1- (ash 1 58)) (ash-flo 1.0 58))))
+
+ ;; prior to guile 2.0.10, inum/complex comparisons were done just by
+ ;; converting the inum to a double, which on a 64-bit would round making
+ ;; say inexact 2^58 appear equal to exact 2^58+1
+ (pass-if (= (+ +0.0i (ash-flo 1.0 58)) (ash 1 58)))
+ (pass-if (not (= (+ +0.0i (ash-flo 1.0 58)) (1+ (ash 1 58)))))
+ (pass-if (not (= (+ +0.0i (ash-flo 1.0 58)) (1- (ash 1 58)))))
+ (pass-if (= (ash 1 58) (+ +0.0i (ash-flo 1.0 58))))
+ (pass-if (not (= (1+ (ash 1 58)) (+ +0.0i (ash-flo 1.0 58)))))
+ (pass-if (not (= (1- (ash 1 58)) (+ +0.0i (ash-flo 1.0 58)))))
+
+ ;; prior to guile 2.0.10, fraction/flonum and fraction/complex
+ ;; comparisons mishandled infinities.
+ (pass-if (not (= 1/2 +inf.0)))
+ (pass-if (not (= 1/2 -inf.0)))
+ (pass-if (not (= +inf.0 1/2)))
+ (pass-if (not (= -inf.0 1/2)))
+ (pass-if (not (= 1/2 +inf.0+0.0i)))
+ (pass-if (not (= 1/2 -inf.0+0.0i)))
+ (pass-if (not (= +inf.0+0.0i 1/2)))
+ (pass-if (not (= -inf.0+0.0i 1/2))))
;;;
;;; <
@@ -2085,6 +2109,9 @@
(pass-if "n = 0.0"
(not (< 0.0 0.0)))
+ (pass-if "n = -0.0"
+ (not (< 0.0 -0.0)))
+
(pass-if "n = 1"
(< 0.0 1))
@@ -2108,6 +2135,9 @@
(pass-if "n = fixnum-min - 1"
(not (< 0.0 (- fixnum-min 1)))))
+
+ (pass-if (not (< -0.0 0.0)))
+ (pass-if (not (< -0.0 -0.0)))
(with-test-prefix "(< 1 n)"
@@ -2433,6 +2463,42 @@
(pass-if (eq? #f (< x (* -4/3 x))))
(pass-if (eq? #f (< (- x) (* -4/3 x))))))
+ (with-test-prefix "inum/flonum"
+ (pass-if (< 4 4.5))
+ (pass-if (< 4.5 5))
+ (pass-if (< -5 -4.5))
+ (pass-if (< -4.5 4))
+ (pass-if (not (< 4.5 4)))
+ (pass-if (not (< 5 4.5)))
+ (pass-if (not (< -4.5 -5)))
+ (pass-if (not (< 4 -4.5)))
+
+ (pass-if (< 4 +inf.0))
+ (pass-if (< -4 +inf.0))
+ (pass-if (< -inf.0 4))
+ (pass-if (< -inf.0 -4))
+ (pass-if (not (< +inf.0 4)))
+ (pass-if (not (< +inf.0 -4)))
+ (pass-if (not (< 4 -inf.0)))
+ (pass-if (not (< -4 -inf.0)))
+
+ (pass-if (not (< +nan.0 4)))
+ (pass-if (not (< +nan.0 -4)))
+ (pass-if (not (< 4 +nan.0)))
+ (pass-if (not (< -4 +nan.0)))
+
+ (pass-if (< most-positive-fixnum (expt 2.0 fixnum-bit)))
+ (pass-if (not (< (expt 2.0 fixnum-bit) most-positive-fixnum)))
+
+ (pass-if (< (- (expt 2.0 fixnum-bit)) most-negative-fixnum))
+ (pass-if (not (< most-negative-fixnum (- (expt 2.0 fixnum-bit)))))
+
+ ;; Prior to guile 2.0.10, we would unconditionally convert the inum
+ ;; to a double, which on a 64-bit system could result in a
+ ;; significant change in its value, thus corrupting the comparison.
+ (pass-if (< most-positive-fixnum (exact->inexact most-positive-fixnum)))
+ (pass-if (< (exact->inexact (- most-positive-fixnum)) (-
most-positive-fixnum))))
+
(with-test-prefix "flonum/frac"
(pass-if (< 0.75 4/3))
(pass-if (< -0.75 4/3))
@@ -4021,6 +4087,19 @@
(let ((big (ash 1 4096)))
(= 1.0 (exact->inexact (/ (1+ big) big)))))
+ ;; In guile 2.0.9, 'exact->inexact' guaranteed proper rounding when
+ ;; applied to non-negative fractions, but on 64-bit systems would
+ ;; sometimes double-round when applied to negative fractions,
+ ;; specifically when the numerator was a fixnum not exactly
+ ;; representable as a double.
+ (with-test-prefix "frac inum/inum, numerator not exactly representable as a
double"
+ (let ((n (+ 1 (expt 2 dbl-mant-dig))))
+ (for-each (lambda (d)
+ (test (/ n d)
+ (/ n d)
+ (exact->inexact (/ n d))))
+ '(3 5 6 7 9 11 13 17 19 23 0.0 -0.0 +nan.0 +inf.0 -inf.0))))
+
(test "round up to odd"
;; =====================================================
;; 11111111111111111111111111111111111111111111111111000101 ->
hooks/post-receive
--
GNU Guile
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Guile-commits] GNU Guile branch, stable-2.0, updated. v2.0.9-26-g95ed221,
Mark H Weaver <=