diff options
| author | Paul Eggert | 2018-08-18 16:13:04 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-08-18 16:13:34 -0700 |
| commit | 1d2df2fd03f35ca8d8dfc8b999d8bba3c7c13157 (patch) | |
| tree | d399dd6434fc2da905c660eea0ce919e746c8951 /src | |
| parent | 97d273033b523bc07911c848d4e8bf96cdce0c90 (diff) | |
| download | emacs-1d2df2fd03f35ca8d8dfc8b999d8bba3c7c13157.tar.gz emacs-1d2df2fd03f35ca8d8dfc8b999d8bba3c7c13157.zip | |
Improve bignum comparison (Bug#32463#50)
* src/data.c (isnan): Remove, as we can assume C99.
(bignumcompare): Remove, folding its functionality
into arithcompare.
(arithcompare): Compare bignums directly here.
Fix bugs when comparing NaNs to bignums.
When comparing a bignum to a fixnum, just look at the
bignum’s sign, as that’s all that is needed.
Decrease scope of locals when this is easy.
* test/src/data-tests.el (data-tests-bignum): Test bignum vs NaN.
Diffstat (limited to 'src')
| -rw-r--r-- | src/data.c | 168 |
1 files changed, 43 insertions, 125 deletions
diff --git a/src/data.c b/src/data.c index a39978ab1dc..0754d4c176d 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -2386,140 +2386,37 @@ bool-vector. IDX starts at 0. */) | |||
| 2386 | 2386 | ||
| 2387 | /* Arithmetic functions */ | 2387 | /* Arithmetic functions */ |
| 2388 | 2388 | ||
| 2389 | #ifndef isnan | ||
| 2390 | # define isnan(x) ((x) != (x)) | ||
| 2391 | #endif | ||
| 2392 | |||
| 2393 | static Lisp_Object | ||
| 2394 | bignumcompare (Lisp_Object num1, Lisp_Object num2, | ||
| 2395 | enum Arith_Comparison comparison) | ||
| 2396 | { | ||
| 2397 | int cmp; | ||
| 2398 | bool test; | ||
| 2399 | |||
| 2400 | if (BIGNUMP (num1)) | ||
| 2401 | { | ||
| 2402 | if (FLOATP (num2)) | ||
| 2403 | { | ||
| 2404 | /* Note that GMP doesn't define comparisons against NaN, so | ||
| 2405 | we need to handle them specially. */ | ||
| 2406 | if (isnan (XFLOAT_DATA (num2))) | ||
| 2407 | return Qnil; | ||
| 2408 | cmp = mpz_cmp_d (XBIGNUM (num1)->value, XFLOAT_DATA (num2)); | ||
| 2409 | } | ||
| 2410 | else if (FIXNUMP (num2)) | ||
| 2411 | { | ||
| 2412 | if (sizeof (EMACS_INT) > sizeof (long) && XFIXNUM (num2) > LONG_MAX) | ||
| 2413 | { | ||
| 2414 | mpz_t tem; | ||
| 2415 | mpz_init (tem); | ||
| 2416 | mpz_set_intmax (tem, XFIXNUM (num2)); | ||
| 2417 | cmp = mpz_cmp (XBIGNUM (num1)->value, tem); | ||
| 2418 | mpz_clear (tem); | ||
| 2419 | } | ||
| 2420 | else | ||
| 2421 | cmp = mpz_cmp_si (XBIGNUM (num1)->value, XFIXNUM (num2)); | ||
| 2422 | } | ||
| 2423 | else | ||
| 2424 | { | ||
| 2425 | eassume (BIGNUMP (num2)); | ||
| 2426 | cmp = mpz_cmp (XBIGNUM (num1)->value, XBIGNUM (num2)->value); | ||
| 2427 | } | ||
| 2428 | } | ||
| 2429 | else | ||
| 2430 | { | ||
| 2431 | eassume (BIGNUMP (num2)); | ||
| 2432 | if (FLOATP (num1)) | ||
| 2433 | { | ||
| 2434 | /* Note that GMP doesn't define comparisons against NaN, so | ||
| 2435 | we need to handle them specially. */ | ||
| 2436 | if (isnan (XFLOAT_DATA (num1))) | ||
| 2437 | return Qnil; | ||
| 2438 | cmp = - mpz_cmp_d (XBIGNUM (num2)->value, XFLOAT_DATA (num1)); | ||
| 2439 | } | ||
| 2440 | else | ||
| 2441 | { | ||
| 2442 | eassume (FIXNUMP (num1)); | ||
| 2443 | if (sizeof (EMACS_INT) > sizeof (long) && XFIXNUM (num1) > LONG_MAX) | ||
| 2444 | { | ||
| 2445 | mpz_t tem; | ||
| 2446 | mpz_init (tem); | ||
| 2447 | mpz_set_intmax (tem, XFIXNUM (num1)); | ||
| 2448 | cmp = - mpz_cmp (XBIGNUM (num2)->value, tem); | ||
| 2449 | mpz_clear (tem); | ||
| 2450 | } | ||
| 2451 | else | ||
| 2452 | cmp = - mpz_cmp_si (XBIGNUM (num2)->value, XFIXNUM (num1)); | ||
| 2453 | } | ||
| 2454 | } | ||
| 2455 | |||
| 2456 | switch (comparison) | ||
| 2457 | { | ||
| 2458 | case ARITH_EQUAL: | ||
| 2459 | test = cmp == 0; | ||
| 2460 | break; | ||
| 2461 | |||
| 2462 | case ARITH_NOTEQUAL: | ||
| 2463 | test = cmp != 0; | ||
| 2464 | break; | ||
| 2465 | |||
| 2466 | case ARITH_LESS: | ||
| 2467 | test = cmp < 0; | ||
| 2468 | break; | ||
| 2469 | |||
| 2470 | case ARITH_LESS_OR_EQUAL: | ||
| 2471 | test = cmp <= 0; | ||
| 2472 | break; | ||
| 2473 | |||
| 2474 | case ARITH_GRTR: | ||
| 2475 | test = cmp > 0; | ||
| 2476 | break; | ||
| 2477 | |||
| 2478 | case ARITH_GRTR_OR_EQUAL: | ||
| 2479 | test = cmp >= 0; | ||
| 2480 | break; | ||
| 2481 | |||
| 2482 | default: | ||
| 2483 | eassume (false); | ||
| 2484 | } | ||
| 2485 | |||
| 2486 | return test ? Qt : Qnil; | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | Lisp_Object | 2389 | Lisp_Object |
| 2490 | arithcompare (Lisp_Object num1, Lisp_Object num2, | 2390 | arithcompare (Lisp_Object num1, Lisp_Object num2, |
| 2491 | enum Arith_Comparison comparison) | 2391 | enum Arith_Comparison comparison) |
| 2492 | { | 2392 | { |
| 2493 | double f1, f2; | 2393 | EMACS_INT i1 = 0, i2 = 0; |
| 2494 | EMACS_INT i1, i2; | 2394 | bool lt, eq = true, gt; |
| 2495 | bool lt, eq, gt; | ||
| 2496 | bool test; | 2395 | bool test; |
| 2497 | 2396 | ||
| 2498 | CHECK_NUMBER_COERCE_MARKER (num1); | 2397 | CHECK_NUMBER_COERCE_MARKER (num1); |
| 2499 | CHECK_NUMBER_COERCE_MARKER (num2); | 2398 | CHECK_NUMBER_COERCE_MARKER (num2); |
| 2500 | 2399 | ||
| 2501 | if (BIGNUMP (num1) || BIGNUMP (num2)) | 2400 | /* If the comparison is mostly done by comparing two doubles, |
| 2502 | return bignumcompare (num1, num2, comparison); | 2401 | set LT, EQ, and GT to the <, ==, > results of that comparison, |
| 2503 | |||
| 2504 | /* If either arg is floating point, set F1 and F2 to the 'double' | ||
| 2505 | approximations of the two arguments, and set LT, EQ, and GT to | ||
| 2506 | the <, ==, > floating-point comparisons of F1 and F2 | ||
| 2507 | respectively, taking care to avoid problems if either is a NaN, | 2402 | respectively, taking care to avoid problems if either is a NaN, |
| 2508 | and trying to avoid problems on platforms where variables (in | 2403 | and trying to avoid problems on platforms where variables (in |
| 2509 | violation of the C standard) can contain excess precision. | 2404 | violation of the C standard) can contain excess precision. |
| 2510 | Regardless, set I1 and I2 to integers that break ties if the | 2405 | Regardless, set I1 and I2 to integers that break ties if the |
| 2511 | floating-point comparison is either not done or reports | 2406 | two-double comparison is either not done or reports |
| 2512 | equality. */ | 2407 | equality. */ |
| 2513 | 2408 | ||
| 2514 | if (FLOATP (num1)) | 2409 | if (FLOATP (num1)) |
| 2515 | { | 2410 | { |
| 2516 | f1 = XFLOAT_DATA (num1); | 2411 | double f1 = XFLOAT_DATA (num1); |
| 2517 | if (FLOATP (num2)) | 2412 | if (FLOATP (num2)) |
| 2518 | { | 2413 | { |
| 2519 | i1 = i2 = 0; | 2414 | double f2 = XFLOAT_DATA (num2); |
| 2520 | f2 = XFLOAT_DATA (num2); | 2415 | lt = f1 < f2; |
| 2416 | eq = f1 == f2; | ||
| 2417 | gt = f1 > f2; | ||
| 2521 | } | 2418 | } |
| 2522 | else | 2419 | else if (FIXNUMP (num2)) |
| 2523 | { | 2420 | { |
| 2524 | /* Compare a float NUM1 to an integer NUM2 by converting the | 2421 | /* Compare a float NUM1 to an integer NUM2 by converting the |
| 2525 | integer I2 (i.e., NUM2) to the double F2 (a conversion that | 2422 | integer I2 (i.e., NUM2) to the double F2 (a conversion that |
| @@ -2529,35 +2426,56 @@ arithcompare (Lisp_Object num1, Lisp_Object num2, | |||
| 2529 | floating-point comparison reports a tie, NUM1 = F1 = F2 = I1 | 2426 | floating-point comparison reports a tie, NUM1 = F1 = F2 = I1 |
| 2530 | (exactly) so I1 - I2 = NUM1 - NUM2 (exactly), so comparing I1 | 2427 | (exactly) so I1 - I2 = NUM1 - NUM2 (exactly), so comparing I1 |
| 2531 | to I2 will break the tie correctly. */ | 2428 | to I2 will break the tie correctly. */ |
| 2532 | i1 = f2 = i2 = XFIXNUM (num2); | 2429 | double f2 = XFIXNUM (num2); |
| 2430 | lt = f1 < f2; | ||
| 2431 | eq = f1 == f2; | ||
| 2432 | gt = f1 > f2; | ||
| 2433 | i1 = f2; | ||
| 2434 | i2 = XFIXNUM (num2); | ||
| 2533 | } | 2435 | } |
| 2534 | lt = f1 < f2; | 2436 | else if (isnan (f1)) |
| 2535 | eq = f1 == f2; | 2437 | lt = eq = gt = false; |
| 2536 | gt = f1 > f2; | 2438 | else |
| 2439 | i2 = mpz_cmp_d (XBIGNUM (num2)->value, f1); | ||
| 2537 | } | 2440 | } |
| 2538 | else | 2441 | else if (FIXNUMP (num1)) |
| 2539 | { | 2442 | { |
| 2540 | i1 = XFIXNUM (num1); | ||
| 2541 | if (FLOATP (num2)) | 2443 | if (FLOATP (num2)) |
| 2542 | { | 2444 | { |
| 2543 | /* Compare an integer NUM1 to a float NUM2. This is the | 2445 | /* Compare an integer NUM1 to a float NUM2. This is the |
| 2544 | converse of comparing float to integer (see above). */ | 2446 | converse of comparing float to integer (see above). */ |
| 2545 | i2 = f1 = i1; | 2447 | double f1 = XFIXNUM (num1), f2 = XFLOAT_DATA (num2); |
| 2546 | f2 = XFLOAT_DATA (num2); | ||
| 2547 | lt = f1 < f2; | 2448 | lt = f1 < f2; |
| 2548 | eq = f1 == f2; | 2449 | eq = f1 == f2; |
| 2549 | gt = f1 > f2; | 2450 | gt = f1 > f2; |
| 2451 | i1 = XFIXNUM (num1); | ||
| 2452 | i2 = f1; | ||
| 2550 | } | 2453 | } |
| 2551 | else | 2454 | else if (FIXNUMP (num2)) |
| 2552 | { | 2455 | { |
| 2456 | i1 = XFIXNUM (num1); | ||
| 2553 | i2 = XFIXNUM (num2); | 2457 | i2 = XFIXNUM (num2); |
| 2554 | eq = true; | ||
| 2555 | } | 2458 | } |
| 2459 | else | ||
| 2460 | i2 = mpz_sgn (XBIGNUM (num2)->value); | ||
| 2556 | } | 2461 | } |
| 2462 | else if (FLOATP (num2)) | ||
| 2463 | { | ||
| 2464 | double f2 = XFLOAT_DATA (num2); | ||
| 2465 | if (isnan (f2)) | ||
| 2466 | lt = eq = gt = false; | ||
| 2467 | else | ||
| 2468 | i1 = mpz_cmp_d (XBIGNUM (num1)->value, f2); | ||
| 2469 | } | ||
| 2470 | else if (FIXNUMP (num2)) | ||
| 2471 | i1 = mpz_sgn (XBIGNUM (num1)->value); | ||
| 2472 | else | ||
| 2473 | i1 = mpz_cmp (XBIGNUM (num1)->value, XBIGNUM (num2)->value); | ||
| 2557 | 2474 | ||
| 2558 | if (eq) | 2475 | if (eq) |
| 2559 | { | 2476 | { |
| 2560 | /* Break a floating-point tie by comparing the integers. */ | 2477 | /* The two-double comparison either reported equality, or was not done. |
| 2478 | Break the tie by comparing the integers. */ | ||
| 2561 | lt = i1 < i2; | 2479 | lt = i1 < i2; |
| 2562 | eq = i1 == i2; | 2480 | eq = i1 == i2; |
| 2563 | gt = i1 > i2; | 2481 | gt = i1 > i2; |