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'
--- a/src/version.c
+++ b/src/version.c
@@ -780,6 +780,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    990,
+/**/
     989,
 /**/
     988,