aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2018-07-18 03:16:54 -0700
committerPaul Eggert2018-07-18 03:18:53 -0700
commitc70d22f70b77b053d01c7380122d166ecb728610 (patch)
tree1fe0dee176da15fca2907c84abc7ec096b7e4116 /src
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.
Diffstat (limited to 'src')
-rw-r--r--src/fns.c68
1 files changed, 35 insertions, 33 deletions
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}