Compare fixnums and floats accurately in value<

Make `value<` compare fixnums and floats by value, as `<` does, instead
of coercing the fixnum to a float first which is what C would do.
This matters when the fixnum cannot be represented as a float. For
example, C would evaluate

  72057594037927935 < 72057594037927936.0

to false since the operands are converted to the same floating-point
number.

* src/fns.c (fixnum_float_cmp): New.
(value_cmp): Use it.
* test/src/fns-tests.el (fns-value<-ordered, fns-value<-unordered):
New test cases.
This commit is contained in:
Mattias Engdegård 2024-07-04 14:46:54 +02:00
parent 565f4e4ad1
commit f9f4f054bc
2 changed files with 29 additions and 2 deletions

View file

@ -3012,6 +3012,23 @@ bool_vector_cmp (Lisp_Object a, Lisp_Object b)
return (d & aw) ? 1 : -1;
}
/* Return -1 if a<b, 1 if a>b, 0 if a=b or if b is NaN. */
static inline int
fixnum_float_cmp (EMACS_INT a, double b)
{
double fa = (double)a;
if (fa == b)
{
/* This doesn't mean that a=b because the conversion may have
rounded, but b must be an integer that fits in an EMACS_INT
and we can compare in the integer domain instead. */
EMACS_INT ib = b; /* lossless conversion */
return a < ib ? -1 : a > ib;
}
else
return fa < b ? -1 : fa > b; /* return 0 if b is NaN */
}
/* Return -1, 0 or 1 to indicate whether a<b, a=b or a>b in the sense of value<.
In particular 0 does not mean equality in the sense of Fequal, only
that the arguments cannot be ordered yet they can be compared (same
@ -3035,7 +3052,7 @@ value_cmp (Lisp_Object a, Lisp_Object b, int maxdepth)
if (FIXNUMP (b))
return ia < XFIXNUM (b) ? -1 : 1; /* we know that a≠b */
if (FLOATP (b))
return ia < XFLOAT_DATA (b) ? -1 : ia > XFLOAT_DATA (b);
return fixnum_float_cmp (ia, XFLOAT_DATA (b));
if (BIGNUMP (b))
return -mpz_sgn (*xbignum_val (b));
}
@ -3176,7 +3193,7 @@ value_cmp (Lisp_Object a, Lisp_Object b, int maxdepth)
if (FLOATP (b))
return fa < XFLOAT_DATA (b) ? -1 : fa > XFLOAT_DATA (b);
if (FIXNUMP (b))
return fa < XFIXNUM (b) ? -1 : fa > XFIXNUM (b);
return -fixnum_float_cmp (XFIXNUM (b), fa);
if (BIGNUMP (b))
{
if (isnan (fa))

View file

@ -1606,6 +1606,13 @@
(1.5 . 1.6) (-1.3 . -1.2) (-13.0 . 12.0)
;; floats/fixnums
(1 . 1.1) (1.9 . 2) (-2.0 . 1) (-2 . 1.0)
;; fixnums that can't be represented as floats
(72057594037927935 . 72057594037927936.0)
(72057594037927936.0 . 72057594037927937)
(-72057594037927936.0 . -72057594037927935)
(-72057594037927937 . -72057594037927936.0)
(2305843009213693951 . 2305843009213693952.0)
;; floats/bignums
(,big . ,(float (* 2 big))) (,(float big) . ,(* 2 big))
;; symbols
@ -1683,6 +1690,9 @@
;; numbers
(0 . 0.0) (0 . -0.0) (0.0 . -0.0)
(72057594037927936 . 72057594037927936.0)
(1 . 0.0e+NaN)
;; symbols
(a . #:a)