diff options
| author | Mattias Engdegård | 2024-07-04 14:46:54 +0200 |
|---|---|---|
| committer | Mattias Engdegård | 2024-07-04 15:32:45 +0200 |
| commit | f9f4f054bc15791ff63252e101dbf08bb4eb33c1 (patch) | |
| tree | 54413cfb6d646fb526c82ea3f91fa207a34b1593 /src | |
| parent | 565f4e4ad17c929159d646bfeff7d1adb4c9b727 (diff) | |
| download | emacs-f9f4f054bc15791ff63252e101dbf08bb4eb33c1.tar.gz emacs-f9f4f054bc15791ff63252e101dbf08bb4eb33c1.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 21 |
1 files changed, 19 insertions, 2 deletions
| @@ -3012,6 +3012,23 @@ bool_vector_cmp (Lisp_Object a, Lisp_Object b) | |||
| 3012 | return (d & aw) ? 1 : -1; | 3012 | return (d & aw) ? 1 : -1; |
| 3013 | } | 3013 | } |
| 3014 | 3014 | ||
| 3015 | /* Return -1 if a<b, 1 if a>b, 0 if a=b or if b is NaN. */ | ||
| 3016 | static inline int | ||
| 3017 | fixnum_float_cmp (EMACS_INT a, double b) | ||
| 3018 | { | ||
| 3019 | double fa = (double)a; | ||
| 3020 | if (fa == b) | ||
| 3021 | { | ||
| 3022 | /* This doesn't mean that a=b because the conversion may have | ||
| 3023 | rounded, but b must be an integer that fits in an EMACS_INT | ||
| 3024 | and we can compare in the integer domain instead. */ | ||
| 3025 | EMACS_INT ib = b; /* lossless conversion */ | ||
| 3026 | return a < ib ? -1 : a > ib; | ||
| 3027 | } | ||
| 3028 | else | ||
| 3029 | return fa < b ? -1 : fa > b; /* return 0 if b is NaN */ | ||
| 3030 | } | ||
| 3031 | |||
| 3015 | /* Return -1, 0 or 1 to indicate whether a<b, a=b or a>b in the sense of value<. | 3032 | /* Return -1, 0 or 1 to indicate whether a<b, a=b or a>b in the sense of value<. |
| 3016 | In particular 0 does not mean equality in the sense of Fequal, only | 3033 | In particular 0 does not mean equality in the sense of Fequal, only |
| 3017 | that the arguments cannot be ordered yet they can be compared (same | 3034 | 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) | |||
| 3035 | if (FIXNUMP (b)) | 3052 | if (FIXNUMP (b)) |
| 3036 | return ia < XFIXNUM (b) ? -1 : 1; /* we know that a≠b */ | 3053 | return ia < XFIXNUM (b) ? -1 : 1; /* we know that a≠b */ |
| 3037 | if (FLOATP (b)) | 3054 | if (FLOATP (b)) |
| 3038 | return ia < XFLOAT_DATA (b) ? -1 : ia > XFLOAT_DATA (b); | 3055 | return fixnum_float_cmp (ia, XFLOAT_DATA (b)); |
| 3039 | if (BIGNUMP (b)) | 3056 | if (BIGNUMP (b)) |
| 3040 | return -mpz_sgn (*xbignum_val (b)); | 3057 | return -mpz_sgn (*xbignum_val (b)); |
| 3041 | } | 3058 | } |
| @@ -3176,7 +3193,7 @@ value_cmp (Lisp_Object a, Lisp_Object b, int maxdepth) | |||
| 3176 | if (FLOATP (b)) | 3193 | if (FLOATP (b)) |
| 3177 | return fa < XFLOAT_DATA (b) ? -1 : fa > XFLOAT_DATA (b); | 3194 | return fa < XFLOAT_DATA (b) ? -1 : fa > XFLOAT_DATA (b); |
| 3178 | if (FIXNUMP (b)) | 3195 | if (FIXNUMP (b)) |
| 3179 | return fa < XFIXNUM (b) ? -1 : fa > XFIXNUM (b); | 3196 | return -fixnum_float_cmp (XFIXNUM (b), fa); |
| 3180 | if (BIGNUMP (b)) | 3197 | if (BIGNUMP (b)) |
| 3181 | { | 3198 | { |
| 3182 | if (isnan (fa)) | 3199 | if (isnan (fa)) |