aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2018-08-18 15:20:46 -0700
committerPaul Eggert2018-08-18 15:22:35 -0700
commit673b1785db4604efe81b8045a9d8ab68936af719 (patch)
tree0f78d72a7d4eef42b62bcfbaec2627aa04986c80 /src
parent877cd22f553624b6d7f24141acd134f9cf839259 (diff)
downloademacs-673b1785db4604efe81b8045a9d8ab68936af719.tar.gz
emacs-673b1785db4604efe81b8045a9d8ab68936af719.zip
Restore traditional lsh behavior on fixnums
* doc/lispref/numbers.texi (Bitwise Operations): Document that the traditional (lsh A B) behavior is for fixnums, and that it is an error if A and B are both negative and A is a bignum. See Bug#32463. * lisp/subr.el (lsh): New function, moved here from src/data.c. * src/data.c (ash_lsh_impl): Remove, moving body into Fash since it’s the only caller now. (Fash): Check for out-of-range counts. If COUNT is zero, return first argument instead of going through libgmp. Omit lsh code since lsh is now done in Lisp. Add code for shifting fixnums right, to avoid a round trip through libgmp. (Flsh): Remove; moved to lisp/subr.el. * test/lisp/international/ccl-tests.el (shift): Test for traditional lsh behavior, instead of assuming lsh is like ash when bignums are present. * test/src/data-tests.el (data-tests-logand) (data-tests-logior, data-tests-logxor, data-tests-ash-lsh): New tests.
Diffstat (limited to 'src')
-rw-r--r--src/data.c60
1 files changed, 24 insertions, 36 deletions
diff --git a/src/data.c b/src/data.c
index 5a355d9787c..a39978ab1dc 100644
--- a/src/data.c
+++ b/src/data.c
@@ -3365,30 +3365,44 @@ representation. */)
3365 : count_one_bits_ll (v)); 3365 : count_one_bits_ll (v));
3366} 3366}
3367 3367
3368static Lisp_Object 3368DEFUN ("ash", Fash, Sash, 2, 2, 0,
3369ash_lsh_impl (Lisp_Object value, Lisp_Object count, bool lsh) 3369 doc: /* Return VALUE with its bits shifted left by COUNT.
3370If COUNT is negative, shifting is actually to the right.
3371In this case, the sign bit is duplicated. */)
3372 (Lisp_Object value, Lisp_Object count)
3370{ 3373{
3371 /* This code assumes that signed right shifts are arithmetic. */
3372 verify ((EMACS_INT) -1 >> 1 == -1);
3373
3374 Lisp_Object val; 3374 Lisp_Object val;
3375 3375
3376 /* The negative of the minimum value of COUNT that fits into a fixnum,
3377 such that mpz_fdiv_q_exp supports -COUNT. */
3378 EMACS_INT minus_count_min = min (-MOST_NEGATIVE_FIXNUM,
3379 TYPE_MAXIMUM (mp_bitcnt_t));
3376 CHECK_INTEGER (value); 3380 CHECK_INTEGER (value);
3377 CHECK_FIXNUM (count); 3381 CHECK_RANGED_INTEGER (count, - minus_count_min, TYPE_MAXIMUM (mp_bitcnt_t));
3378 3382
3379 if (BIGNUMP (value)) 3383 if (BIGNUMP (value))
3380 { 3384 {
3385 if (XFIXNUM (count) == 0)
3386 return value;
3381 mpz_t result; 3387 mpz_t result;
3382 mpz_init (result); 3388 mpz_init (result);
3383 if (XFIXNUM (count) >= 0) 3389 if (XFIXNUM (count) > 0)
3384 mpz_mul_2exp (result, XBIGNUM (value)->value, XFIXNUM (count)); 3390 mpz_mul_2exp (result, XBIGNUM (value)->value, XFIXNUM (count));
3385 else if (lsh)
3386 mpz_tdiv_q_2exp (result, XBIGNUM (value)->value, - XFIXNUM (count));
3387 else 3391 else
3388 mpz_fdiv_q_2exp (result, XBIGNUM (value)->value, - XFIXNUM (count)); 3392 mpz_fdiv_q_2exp (result, XBIGNUM (value)->value, - XFIXNUM (count));
3389 val = make_number (result); 3393 val = make_number (result);
3390 mpz_clear (result); 3394 mpz_clear (result);
3391 } 3395 }
3396 else if (XFIXNUM (count) <= 0)
3397 {
3398 /* This code assumes that signed right shifts are arithmetic. */
3399 verify ((EMACS_INT) -1 >> 1 == -1);
3400
3401 EMACS_INT shift = -XFIXNUM (count);
3402 EMACS_INT result = (shift < EMACS_INT_WIDTH ? XFIXNUM (value) >> shift
3403 : XFIXNUM (value) < 0 ? -1 : 0);
3404 val = make_fixnum (result);
3405 }
3392 else 3406 else
3393 { 3407 {
3394 /* Just do the work as bignums to make the code simpler. */ 3408 /* Just do the work as bignums to make the code simpler. */
@@ -3400,14 +3414,7 @@ ash_lsh_impl (Lisp_Object value, Lisp_Object count, bool lsh)
3400 3414
3401 if (XFIXNUM (count) >= 0) 3415 if (XFIXNUM (count) >= 0)
3402 mpz_mul_2exp (result, result, XFIXNUM (count)); 3416 mpz_mul_2exp (result, result, XFIXNUM (count));
3403 else if (lsh) 3417 else
3404 {
3405 if (mpz_sgn (result) > 0)
3406 mpz_fdiv_q_2exp (result, result, - XFIXNUM (count));
3407 else
3408 mpz_fdiv_q_2exp (result, result, - XFIXNUM (count));
3409 }
3410 else /* ash */
3411 mpz_fdiv_q_2exp (result, result, - XFIXNUM (count)); 3418 mpz_fdiv_q_2exp (result, result, - XFIXNUM (count));
3412 3419
3413 val = make_number (result); 3420 val = make_number (result);
@@ -3417,24 +3424,6 @@ ash_lsh_impl (Lisp_Object value, Lisp_Object count, bool lsh)
3417 return val; 3424 return val;
3418} 3425}
3419 3426
3420DEFUN ("ash", Fash, Sash, 2, 2, 0,
3421 doc: /* Return VALUE with its bits shifted left by COUNT.
3422If COUNT is negative, shifting is actually to the right.
3423In this case, the sign bit is duplicated. */)
3424 (register Lisp_Object value, Lisp_Object count)
3425{
3426 return ash_lsh_impl (value, count, false);
3427}
3428
3429DEFUN ("lsh", Flsh, Slsh, 2, 2, 0,
3430 doc: /* Return VALUE with its bits shifted left by COUNT.
3431If COUNT is negative, shifting is actually to the right.
3432In this case, zeros are shifted in on the left. */)
3433 (register Lisp_Object value, Lisp_Object count)
3434{
3435 return ash_lsh_impl (value, count, true);
3436}
3437
3438DEFUN ("1+", Fadd1, Sadd1, 1, 1, 0, 3427DEFUN ("1+", Fadd1, Sadd1, 1, 1, 0,
3439 doc: /* Return NUMBER plus one. NUMBER may be a number or a marker. 3428 doc: /* Return NUMBER plus one. NUMBER may be a number or a marker.
3440Markers are converted to integers. */) 3429Markers are converted to integers. */)
@@ -4235,7 +4224,6 @@ syms_of_data (void)
4235 defsubr (&Slogior); 4224 defsubr (&Slogior);
4236 defsubr (&Slogxor); 4225 defsubr (&Slogxor);
4237 defsubr (&Slogcount); 4226 defsubr (&Slogcount);
4238 defsubr (&Slsh);
4239 defsubr (&Sash); 4227 defsubr (&Sash);
4240 defsubr (&Sadd1); 4228 defsubr (&Sadd1);
4241 defsubr (&Ssub1); 4229 defsubr (&Ssub1);