vim-patch:8.1.0990: floating point exception with "%= 0" and "/= 0"

Problem:    Floating point exception with "%= 0" and "/= 0".
Solution:   Avoid dividing by zero. (Dominique Pelle, closes vim/vim#4058)
e21c1580b7
This commit is contained in:
Jan Edmund Lazo 2019-07-28 16:54:06 -04:00
parent 98d389ce55
commit b457a58e34
3 changed files with 73 additions and 20 deletions

View File

@ -526,6 +526,35 @@ const list_T *eval_msgpack_type_lists[] = {
[kMPExt] = NULL, [kMPExt] = NULL,
}; };
// Return "n1" divided by "n2", taking care of dividing by zero.
varnumber_T num_divide(varnumber_T n1, varnumber_T n2)
FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
varnumber_T result;
if (n2 == 0) { // give an error message?
if (n1 == 0) {
result = VARNUMBER_MIN; // similar to NaN
} else if (n1 < 0) {
result = -VARNUMBER_MAX;
} else {
result = VARNUMBER_MAX;
}
} else {
result = n1 / n2;
}
return result;
}
// Return "n1" modulus "n2", taking care of dividing by zero.
varnumber_T num_modulus(varnumber_T n1, varnumber_T n2)
FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
// Give an error when n2 is 0?
return (n2 == 0) ? 0 : (n1 % n2);
}
/* /*
* Initialize the global and v: variables. * Initialize the global and v: variables.
*/ */
@ -2047,8 +2076,8 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
case '+': n = numval + n; break; case '+': n = numval + n; break;
case '-': n = numval - n; break; case '-': n = numval - n; break;
case '*': n = numval * n; break; case '*': n = numval * n; break;
case '/': n = numval / n; break; case '/': n = num_divide(numval, n); break;
case '%': n = numval % n; break; case '%': n = num_modulus(numval, n); break;
} }
} else if (opt_type == 0 && stringval != NULL) { // string } else if (opt_type == 0 && stringval != NULL) { // string
char *const oldstringval = stringval; char *const oldstringval = stringval;
@ -4178,22 +4207,9 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
if (op == '*') { if (op == '*') {
n1 = n1 * n2; n1 = n1 * n2;
} else if (op == '/') { } else if (op == '/') {
if (n2 == 0) { // give an error message? n1 = num_divide(n1, n2);
if (n1 == 0) {
n1 = VARNUMBER_MIN; // similar to NaN
} else if (n1 < 0) {
n1 = -VARNUMBER_MAX;
} else {
n1 = VARNUMBER_MAX;
}
} else {
n1 = n1 / n2;
}
} else { } else {
if (n2 == 0) /* give an error message? */ n1 = num_modulus(n1, n2);
n1 = 0;
else
n1 = n1 % n2;
} }
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n1; rettv->vval.v_number = n1;

View File

@ -74,8 +74,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
case '+': n += tv_get_number(tv2); break; case '+': n += tv_get_number(tv2); break;
case '-': n -= tv_get_number(tv2); break; case '-': n -= tv_get_number(tv2); break;
case '*': n *= tv_get_number(tv2); break; case '*': n *= tv_get_number(tv2); break;
case '/': n /= tv_get_number(tv2); break; case '/': n = num_divide(n, tv_get_number(tv2)); break;
case '%': n %= tv_get_number(tv2); break; case '%': n = num_modulus(n, tv_get_number(tv2)); break;
} }
tv_clear(tv1); tv_clear(tv1);
tv1->v_type = VAR_NUMBER; tv1->v_type = VAR_NUMBER;

View File

@ -21,7 +21,7 @@ com! -nargs=1 Xout call Xout(<args>)
" "
" Create a script that consists of the body of the function a:funcname. " Create a script that consists of the body of the function a:funcname.
" Replace any ":return" by a ":finish", any argument variable by a global " Replace any ":return" by a ":finish", any argument variable by a global
" variable, and and every ":call" by a ":source" for the next following argument " variable, and every ":call" by a ":source" for the next following argument
" in the variable argument list. This function is useful if similar tests are " in the variable argument list. This function is useful if similar tests are
" to be made for a ":return" from a function call or a ":finish" in a script " to be made for a ":return" from a function call or a ":finish" in a script
" file. " file.
@ -1310,6 +1310,43 @@ func Test_compound_assignment_operators()
let x .= 'n' let x .= 'n'
call assert_equal('2n', x) call assert_equal('2n', x)
" Test special cases: division or modulus with 0.
let x = 1
let x /= 0
if has('num64')
call assert_equal(0x7FFFFFFFFFFFFFFF, x)
else
call assert_equal(0x7fffffff, x)
endif
let x = -1
let x /= 0
if has('num64')
call assert_equal(-0x7FFFFFFFFFFFFFFF, x)
else
call assert_equal(-0x7fffffff, x)
endif
let x = 0
let x /= 0
if has('num64')
call assert_equal(-0x7FFFFFFFFFFFFFFF - 1, x)
else
call assert_equal(-0x7FFFFFFF - 1, x)
endif
let x = 1
let x %= 0
call assert_equal(0, x)
let x = -1
let x %= 0
call assert_equal(0, x)
let x = 0
let x %= 0
call assert_equal(0, x)
" Test for string " Test for string
let x = 'str' let x = 'str'
let x .= 'ing' let x .= 'ing'