Skip to content

Commit

Permalink
patch 8.2.5004: right shift on negative number does not work as docum…
Browse files Browse the repository at this point in the history
…ented

Problem:    Right shift on negative number does not work as documented.
Solution:   Use a uvarnumber_T type cast.
  • Loading branch information
brammool committed May 22, 2022
1 parent a061f34 commit 338bf58
Show file tree
Hide file tree
Showing 7 changed files with 26 additions and 19 deletions.
11 changes: 8 additions & 3 deletions runtime/doc/eval.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1138,9 +1138,10 @@ expr6 >> expr6 bitwise right shift *expr->>*
*E1282* *E1283*
The "<<" and ">>" operators can be used to perform bitwise left or right shift
of the left operand by the number of bits specified by the right operand. The
operands must be positive numbers. The topmost bit (sign bit) is always
cleared for ">>". If the right operand (shift amount) is more than the
maximum number of bits in a number (|v:numbersize|) the result is zero.
operands are used as positive numbers. When shifting right with ">>" the
topmost bit (somtimes called the sign bit) is cleared. If the right operand
(shift amount) is more than the maximum number of bits in a number
(|v:numbersize|) the result is zero.


expr6 and expr7 *expr6* *expr7* *E1036* *E1051*
Expand Down Expand Up @@ -1417,6 +1418,10 @@ number number constant *expr-number*
Decimal, Hexadecimal (starting with 0x or 0X), Binary (starting with 0b or 0B)
and Octal (starting with 0, 0o or 0O).

Assuming 64 bit numbers are used (see |v:numbersize|) an unsigned number is
truncated to 0x7fffffffffffffff or 9223372036854775807. You can use -1 to get
0xffffffffffffffff.

*floating-point-format*
Floating point numbers can be written in two forms:

Expand Down
1 change: 1 addition & 0 deletions src/charset.c
Original file line number Diff line number Diff line change
Expand Up @@ -2002,6 +2002,7 @@ vim_str2nr(
}
else
{
// prevent a larg unsigned number to become negative
if (un > VARNUM_MAX)
un = VARNUM_MAX;
*nptr = (varnumber_T)un;
Expand Down
6 changes: 1 addition & 5 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -3091,12 +3091,8 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
rettv->vval.v_number =
rettv->vval.v_number << var2.vval.v_number;
else
{
rettv->vval.v_number =
rettv->vval.v_number >> var2.vval.v_number;
// clear the topmost sign bit
rettv->vval.v_number &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS);
}
(uvarnumber_T)rettv->vval.v_number >> var2.vval.v_number;
}

clear_tv(&var2);
Expand Down
11 changes: 11 additions & 0 deletions src/testdir/test_expr.vim
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,8 @@ func Test_bitwise_shift()
call assert_equal(0, 0 >> 4)
call assert_equal(0, 999999 >> 100)
call assert_equal(0, 999999 << 100)
call assert_equal(-1, -1 >> 0)
call assert_equal(-1, -1 << 0)
VAR a = 8
VAR b = 2
call assert_equal(2, a >> b)
Expand All @@ -976,6 +978,15 @@ func Test_bitwise_shift()
for i in range(0, v:numbersize - 2)
LET val = and(val, invert(1 << i))
endfor
#" -1 has all the bits set
call assert_equal(-2, -1 << 1)
call assert_equal(-4, -1 << 2)
call assert_equal(-8, -1 << 3)
if v:numbersize == 64
call assert_equal(0x7fffffffffffffff, -1 >> 1)
call assert_equal(0x3fffffffffffffff, -1 >> 2)
call assert_equal(0x1fffffffffffffff, -1 >> 3)
endif
call assert_equal(0, val)
#" multiple operators
call assert_equal(16, 1 << 2 << 2)
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
5004,
/**/
5003,
/**/
Expand Down
7 changes: 1 addition & 6 deletions src/vim9execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -4096,12 +4096,7 @@ exec_instructions(ectx_T *ectx)
case EXPR_RSHIFT: if (arg2 > MAX_LSHIFT_BITS)
res = 0;
else
{
res = arg1 >> arg2;
// clear the topmost sign bit
res &= ~((uvarnumber_T)1
<< MAX_LSHIFT_BITS);
}
res = (uvarnumber_T)arg1 >> arg2;
break;
default: break;
}
Expand Down
7 changes: 2 additions & 5 deletions src/vim9expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2719,11 +2719,8 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
else if (type == EXPR_LSHIFT)
tv1->vval.v_number = tv1->vval.v_number << tv2->vval.v_number;
else
{
tv1->vval.v_number = tv1->vval.v_number >> tv2->vval.v_number;
// clear the topmost sign bit
tv1->vval.v_number &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS);
}
tv1->vval.v_number =
(uvarnumber_T)tv1->vval.v_number >> tv2->vval.v_number;
clear_tv(tv2);
--ppconst->pp_used;
}
Expand Down

0 comments on commit 338bf58

Please sign in to comment.