Mercurial > vim
changeset 15969:9cc42db77a54 v8.1.0990
patch 8.1.0990: floating point exception with "%= 0" and "/= 0"
commit https://github.com/vim/vim/commit/e21c1580b7acb598a6e3c38565434fe5d0e2ad7a
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Mar 2 11:57:09 2019 +0100
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 https://github.com/vim/vim/issues/4058)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 02 Mar 2019 12:00:06 +0100 |
parents | 867ac4696428 |
children | 4b4561a386cd |
files | src/eval.c src/testdir/test_vimscript.vim src/version.c |
diffstat | 3 files changed, 80 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/src/eval.c +++ b/src/eval.c @@ -253,6 +253,39 @@ static char_u *find_option_end(char_u ** /* for VIM_VERSION_ defines */ #include "version.h" +/* + * Return "n1" divided by "n2", taking care of dividing by zero. + */ + static varnumber_T +num_divide(varnumber_T n1, varnumber_T n2) +{ + varnumber_T result; + + if (n2 == 0) // give an error message? + { + if (n1 == 0) + result = VARNUM_MIN; // similar to NaN + else if (n1 < 0) + result = -VARNUM_MAX; + else + result = VARNUM_MAX; + } + else + result = n1 / n2; + + return result; +} + +/* + * Return "n1" modulus "n2", taking care of dividing by zero. + */ + static varnumber_T +num_modulus(varnumber_T n1, varnumber_T n2) +{ + // Give an error when n2 is 0? + return (n2 == 0) ? 0 : (n1 % n2); +} + #if defined(EBCDIC) || defined(PROTO) /* @@ -1758,8 +1791,8 @@ ex_let_one( 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 = (long)num_divide(numval, n); break; + case '%': n = (long)num_modulus(numval, n); break; } } else if (opt_type == 0 && stringval != NULL) // string @@ -2538,8 +2571,8 @@ tv_op(typval_T *tv1, typval_T *tv2, char 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 = num_modulus(n, tv_get_number(tv2)); break; } clear_tv(tv1); tv1->v_type = VAR_NUMBER; @@ -4113,26 +4146,10 @@ eval6( if (op == '*') n1 = n1 * n2; else if (op == '/') - { - if (n2 == 0) /* give an error message? */ - { - if (n1 == 0) - n1 = VARNUM_MIN; /* similar to NaN */ - else if (n1 < 0) - n1 = -VARNUM_MAX; - else - n1 = VARNUM_MAX; - } - else - n1 = n1 / n2; - } + n1 = num_divide(n1, n2); else - { - if (n2 == 0) /* give an error message? */ - n1 = 0; - else - n1 = n1 % n2; - } + n1 = num_modulus(n1, n2); + rettv->v_type = VAR_NUMBER; rettv->vval.v_number = n1; }
--- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -21,7 +21,7 @@ com! -nargs=1 Xout call Xout(<a " " 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 -" 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 " to be made for a ":return" from a function call or a ":finish" in a script " file. @@ -1457,6 +1457,43 @@ func Test_compound_assignment_operators( let x .= 'n' 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 let x = 'str' let x .= 'ing'