aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2018-08-18 16:13:04 -0700
committerPaul Eggert2018-08-18 16:13:34 -0700
commit1d2df2fd03f35ca8d8dfc8b999d8bba3c7c13157 (patch)
treed399dd6434fc2da905c660eea0ce919e746c8951 /src
parent97d273033b523bc07911c848d4e8bf96cdce0c90 (diff)
downloademacs-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.c168
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
2393static Lisp_Object
2394bignumcompare (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
2489Lisp_Object 2389Lisp_Object
2490arithcompare (Lisp_Object num1, Lisp_Object num2, 2390arithcompare (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;