aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2018-07-18 03:16:54 -0700
committerPaul Eggert2018-07-18 03:18:53 -0700
commitc70d22f70b77b053d01c7380122d166ecb728610 (patch)
tree1fe0dee176da15fca2907c84abc7ec096b7e4116
parentba6cc1d04cef8e25534a72e90a8f0f8db0026c9f (diff)
downloademacs-c70d22f70b77b053d01c7380122d166ecb728610.tar.gz
emacs-c70d22f70b77b053d01c7380122d166ecb728610.zip
Fix bug with eql etc. on NaNs
Fix a bug where eql, sxhash-eql, memql, and make-hash-table were not consistent on NaNs. Likewise for equal, sxhash-equal, member, and make-hash-table. Some of these functions ignored NaN significands, whereas others treated them as significant. It's more logical to treat significands as significant, and this typically makes eql a bit more efficient on floats, with just one integer comparison instead of one to three floating-point comparisons. * doc/lispref/numbers.texi (Float Basics): Document that NaNs are never numerically equal, but might be eql. * src/fns.c (WORDS_PER_DOUBLE): Move to top level of this file. (union double_and_words): Now named, and at the top level of this file. (same_float): New function. (Fmemql, Feql, internal_equal, cmpfn_eql): Use it, so that the corresponding functions treat NaNs consistently. (sxhash_float): Simplify based on above-mentioned changes. * test/src/fns-tests.el (fns-tests-equality-nan): New test.
-rw-r--r--doc/lispref/numbers.texi9
-rw-r--r--src/fns.c68
-rw-r--r--test/src/fns-tests.el11
3 files changed, 53 insertions, 35 deletions
diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi
index 2fed2b642fd..6c51b849d35 100644
--- a/doc/lispref/numbers.texi
+++ b/doc/lispref/numbers.texi
@@ -232,13 +232,18 @@ distinguish them.
232@cindex negative infinity 232@cindex negative infinity
233@cindex infinity 233@cindex infinity
234@cindex NaN 234@cindex NaN
235@findex eql
236@findex sxhash-eql
235 The @acronym{IEEE} floating-point standard supports positive 237 The @acronym{IEEE} floating-point standard supports positive
236infinity and negative infinity as floating-point values. It also 238infinity and negative infinity as floating-point values. It also
237provides for a class of values called NaN, or ``not a number''; 239provides for a class of values called NaN, or ``not a number'';
238numerical functions return such values in cases where there is no 240numerical functions return such values in cases where there is no
239correct answer. For example, @code{(/ 0.0 0.0)} returns a NaN@. 241correct answer. For example, @code{(/ 0.0 0.0)} returns a NaN@.
240Although NaN values carry a sign, for practical purposes there is no other 242A NaN is never numerically equal to any value, not even to itself.
241significant difference between different NaN values in Emacs Lisp. 243NaNs carry a sign and a significand, and non-numeric functions like
244@code{eql} and @code{sxhash-eql} treat two NaNs as equal when their
245signs and significands agree. Significands of NaNs are
246machine-dependent and are not directly visible to Emacs Lisp.
242 247
243Here are read syntaxes for these special floating-point values: 248Here are read syntaxes for these special floating-point values:
244 249
diff --git a/src/fns.c b/src/fns.c
index c171784d290..10997da0d46 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -1419,6 +1419,29 @@ DEFUN ("elt", Felt, Selt, 2, 2, 0,
1419 return Faref (sequence, n); 1419 return Faref (sequence, n);
1420} 1420}
1421 1421
1422enum { WORDS_PER_DOUBLE = (sizeof (double) / sizeof (EMACS_UINT)
1423 + (sizeof (double) % sizeof (EMACS_UINT) != 0)) };
1424union double_and_words
1425{
1426 double val;
1427 EMACS_UINT word[WORDS_PER_DOUBLE];
1428};
1429
1430/* Return true if X and Y are the same floating-point value.
1431 This looks at X's and Y's representation, since (unlike '==')
1432 it returns true if X and Y are the same NaN. */
1433static bool
1434same_float (Lisp_Object x, Lisp_Object y)
1435{
1436 union double_and_words
1437 xu = { .val = XFLOAT_DATA (x) },
1438 yu = { .val = XFLOAT_DATA (y) };
1439 EMACS_UINT neql = 0;
1440 for (int i = 0; i < WORDS_PER_DOUBLE; i++)
1441 neql |= xu.word[i] ^ yu.word[i];
1442 return !neql;
1443}
1444
1422DEFUN ("member", Fmember, Smember, 2, 2, 0, 1445DEFUN ("member", Fmember, Smember, 2, 2, 0,
1423 doc: /* Return non-nil if ELT is an element of LIST. Comparison done with `equal'. 1446 doc: /* Return non-nil if ELT is an element of LIST. Comparison done with `equal'.
1424The value is actually the tail of LIST whose car is ELT. */) 1447The value is actually the tail of LIST whose car is ELT. */)
@@ -1457,7 +1480,7 @@ The value is actually the tail of LIST whose car is ELT. */)
1457 FOR_EACH_TAIL (tail) 1480 FOR_EACH_TAIL (tail)
1458 { 1481 {
1459 Lisp_Object tem = XCAR (tail); 1482 Lisp_Object tem = XCAR (tail);
1460 if (FLOATP (tem) && equal_no_quit (elt, tem)) 1483 if (FLOATP (tem) && same_float (elt, tem))
1461 return tail; 1484 return tail;
1462 } 1485 }
1463 CHECK_LIST_END (tail, list); 1486 CHECK_LIST_END (tail, list);
@@ -2175,7 +2198,7 @@ Floating-point numbers of equal value are `eql', but they may not be `eq'. */)
2175 (Lisp_Object obj1, Lisp_Object obj2) 2198 (Lisp_Object obj1, Lisp_Object obj2)
2176{ 2199{
2177 if (FLOATP (obj1)) 2200 if (FLOATP (obj1))
2178 return equal_no_quit (obj1, obj2) ? Qt : Qnil; 2201 return FLOATP (obj2) && same_float (obj1, obj2) ? Qt : Qnil;
2179 else 2202 else
2180 return EQ (obj1, obj2) ? Qt : Qnil; 2203 return EQ (obj1, obj2) ? Qt : Qnil;
2181} 2204}
@@ -2266,13 +2289,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind,
2266 switch (XTYPE (o1)) 2289 switch (XTYPE (o1))
2267 { 2290 {
2268 case Lisp_Float: 2291 case Lisp_Float:
2269 { 2292 return same_float (o1, o2);
2270 double d1 = XFLOAT_DATA (o1);
2271 double d2 = XFLOAT_DATA (o2);
2272 /* If d is a NaN, then d != d. Two NaNs should be `equal' even
2273 though they are not =. */
2274 return d1 == d2 || (d1 != d1 && d2 != d2);
2275 }
2276 2293
2277 case Lisp_Cons: 2294 case Lisp_Cons:
2278 if (equal_kind == EQUAL_NO_QUIT) 2295 if (equal_kind == EQUAL_NO_QUIT)
@@ -3706,24 +3723,20 @@ HASH_INDEX (struct Lisp_Hash_Table *h, ptrdiff_t idx)
3706 return XINT (AREF (h->index, idx)); 3723 return XINT (AREF (h->index, idx));
3707} 3724}
3708 3725
3709/* Compare KEY1 which has hash code HASH1 and KEY2 with hash code 3726/* Compare KEY1 and KEY2 in hash table HT using `eql'. Value is true
3710 HASH2 in hash table H using `eql'. Value is true if KEY1 and 3727 if KEY1 and KEY2 are the same. KEY1 and KEY2 must not be eq. */
3711 KEY2 are the same. */
3712 3728
3713static bool 3729static bool
3714cmpfn_eql (struct hash_table_test *ht, 3730cmpfn_eql (struct hash_table_test *ht,
3715 Lisp_Object key1, 3731 Lisp_Object key1,
3716 Lisp_Object key2) 3732 Lisp_Object key2)
3717{ 3733{
3718 return (FLOATP (key1) 3734 return FLOATP (key1) && FLOATP (key2) && same_float (key1, key2);
3719 && FLOATP (key2)
3720 && XFLOAT_DATA (key1) == XFLOAT_DATA (key2));
3721} 3735}
3722 3736
3723 3737
3724/* Compare KEY1 which has hash code HASH1 and KEY2 with hash code 3738/* Compare KEY1 and KEY2 in hash table HT using `equal'. Value is
3725 HASH2 in hash table H using `equal'. Value is true if KEY1 and 3739 true if KEY1 and KEY2 are the same. */
3726 KEY2 are the same. */
3727 3740
3728static bool 3741static bool
3729cmpfn_equal (struct hash_table_test *ht, 3742cmpfn_equal (struct hash_table_test *ht,
@@ -3734,9 +3747,8 @@ cmpfn_equal (struct hash_table_test *ht,
3734} 3747}
3735 3748
3736 3749
3737/* Compare KEY1 which has hash code HASH1, and KEY2 with hash code 3750/* Compare KEY1 and KEY2 in hash table HT using HT->user_cmp_function.
3738 HASH2 in hash table H using H->user_cmp_function. Value is true 3751 Value is true if KEY1 and KEY2 are the same. */
3739 if KEY1 and KEY2 are the same. */
3740 3752
3741static bool 3753static bool
3742cmpfn_user_defined (struct hash_table_test *ht, 3754cmpfn_user_defined (struct hash_table_test *ht,
@@ -4328,18 +4340,8 @@ static EMACS_UINT
4328sxhash_float (double val) 4340sxhash_float (double val)
4329{ 4341{
4330 EMACS_UINT hash = 0; 4342 EMACS_UINT hash = 0;
4331 enum { 4343 union double_and_words u = { .val = val };
4332 WORDS_PER_DOUBLE = (sizeof val / sizeof hash 4344 for (int i = 0; i < WORDS_PER_DOUBLE; i++)
4333 + (sizeof val % sizeof hash != 0))
4334 };
4335 union {
4336 double val;
4337 EMACS_UINT word[WORDS_PER_DOUBLE];
4338 } u;
4339 int i;
4340 u.val = val;
4341 memset (&u.val + 1, 0, sizeof u - sizeof u.val);
4342 for (i = 0; i < WORDS_PER_DOUBLE; i++)
4343 hash = sxhash_combine (hash, u.word[i]); 4345 hash = sxhash_combine (hash, u.word[i]);
4344 return SXHASH_REDUCE (hash); 4346 return SXHASH_REDUCE (hash);
4345} 4347}
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index d9cca557cf2..e4b9cbe25a4 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -23,6 +23,17 @@
23 23
24(require 'cl-lib) 24(require 'cl-lib)
25 25
26;; Test that equality predicates work correctly on NaNs when combined
27;; with hash tables based on those predicates. This was not the case
28;; for eql in Emacs 26.
29(ert-deftest fns-tests-equality-nan ()
30 (dolist (test (list #'eq #'eql #'equal))
31 (let* ((h (make-hash-table :test test))
32 (nan 0.0e+NaN)
33 (-nan (- nan)))
34 (puthash nan t h)
35 (should (eq (funcall test nan -nan) (gethash -nan h))))))
36
26(ert-deftest fns-tests-reverse () 37(ert-deftest fns-tests-reverse ()
27 (should-error (reverse)) 38 (should-error (reverse))
28 (should-error (reverse 1)) 39 (should-error (reverse 1))