Tcl Source Code

Artifact [66f499ef3c]
Login

Artifact 66f499ef3c622a0ead59e4527afa665ff88d4148:

Attachment "expr.patch" to ticket [1230205fff] added by mdejong 2005-06-30 15:15:46.
2005-06-30  Mo DeJong  <[email protected]>

	* generic/tclExecute.c (TclExecuteByteCode):
        Reimplement long and wide type integer division
        and modulus operations so that the smallest
        and largest integer values are handled properly.
        The divide operation is more efficient since
        it no longer does a modulus or negation and
        only checks for a remainder when the quotient
        will be a negative number. The modulus operation
        is now a bit more complex because of a number of
        special cases dealing with the smallest and
        largest integers.
	* tests/expr.test: Add test cases for division
        and modulus operations on the smallest and
        largest integer values for 32 and 64 bit types.

Index: generic/tclExecute.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclExecute.c,v
retrieving revision 1.193
diff -u -r1.193 tclExecute.c
--- generic/tclExecute.c	29 Jun 2005 03:29:00 -0000	1.193
+++ generic/tclExecute.c	30 Jun 2005 07:48:55 -0000
@@ -3556,7 +3556,7 @@
 	 * Only integers are allowed. We compute value op value2.
 	 */
 
-	long i = 0, i2 = 0, rem, negative;
+	long i = 0, i2 = 0, rem, neg_divisor = 0;
 	long iResult = 0; /* Init. avoids compiler warning. */
 	Tcl_WideInt w, w2, wResult = W0;
 	int doWide = 0;
@@ -3599,8 +3599,9 @@
 	case INST_MOD:
 	    /*
 	     * This code is tricky: C doesn't guarantee much about
-	     * the quotient or remainder, but Tcl does. The
-	     * remainder always has the same sign as the divisor and
+	     * the quotient or remainder, and results with a negative
+	     * divisor are not specified. Tcl guarantees that the
+	     * remainder will have the same sign as the divisor and
 	     * a smaller absolute value.
 	     */
 	    if (value2Ptr->typePtr == &tclWideIntType && w2 == W0) {
@@ -3619,7 +3620,6 @@
 		}
 		goto divideByZero;
 	    }
-	    negative = 0;
 	    if (valuePtr->typePtr == &tclWideIntType
 		|| value2Ptr->typePtr == &tclWideIntType) {
 		Tcl_WideInt wRemainder;
@@ -3631,32 +3631,119 @@
 		} else if (value2Ptr->typePtr == &tclIntType) {
 		    w2 = Tcl_LongAsWide(i2);
 		}
-		if (w2 < 0) {
-		    w2 = -w2;
-		    w = -w;
-		    negative = 1;
-		}
-		wRemainder  = w % w2;
-		if (wRemainder < 0) {
-		    wRemainder += w2;
+		/*
+		 * Note that special cases are needed when the
+		 * divisor is LLONG_MIN because of the property
+		 * that -LLONG_MIN == LLONG_MIN and a modulus with
+		 * a negative divisor is not well defined in C.
+		 */
+		if ( w == LLONG_MIN && w2 == -1 ) {
+		    /* Integer overflow could happen with
+		     * (LLONG_MIN % -1) even though it
+		     * is not possible in the code below. */
+		    wRemainder = 0;
+		} else if ( w == LLONG_MAX && w2 == LLONG_MIN ) {
+		    wRemainder = -1;
+		    neg_divisor = 1;
+		} else if ( w == LLONG_MIN && w2 == LLONG_MIN ) {
+		    wRemainder = 0;
+		} else if ( w == LLONG_MIN && w2 == LLONG_MAX ) {
+		    wRemainder = LLONG_MAX - 1;
+		} else if ( w2 == LLONG_MIN ) {
+		    /* w % LLONG_MIN is not well defined since
+		     * the divisor would be a negative number
+		     * and -LLONG_MIN is also a negative number.
+		     */
+		    if (w == 0) {
+		        wRemainder = 0;
+		    } else if (w < 0) {
+		        wRemainder = w;
+		    } else {
+		        wRemainder = LLONG_MIN + w;
+		    }
+		} else {
+		    if (w2 < 0) {
+		        w2 = -w2;
+		        w = -w; /* Note: -LLONG_MIN == LLONG_MIN */
+		        neg_divisor = 1;
+		    }
+		    wRemainder = w % w2;
+
+		    /*
+		     * remainder is (remainder + divisor) when the
+		     * remainder is negative. Watch out for the
+		     * special case of a LLONG_MIN dividend and
+		     * a negative divisor. Don't add the divisor
+		     * in that case because the remainder should
+		     * not be negative.
+		     */
+		    if (wRemainder < 0 && !(neg_divisor && (w == LLONG_MIN))) {
+		        wRemainder += w2;
+		    }
 		}
-		if (negative) {
+		if ((neg_divisor && (wRemainder > 0)) ||
+		        (!neg_divisor && (wRemainder < 0))) {
 		    wRemainder = -wRemainder;
 		}
 		wResult = wRemainder;
 		doWide = 1;
 		break;
 	    }
-	    if (i2 < 0) {
-		i2 = -i2;
-		i = -i;
-		negative = 1;
-	    }
-	    rem  = i % i2;
-	    if (rem < 0) {
-		rem += i2;
+
+	    /*
+	     * Note that special cases are needed when the
+	     * divisor is LONG_MIN because of the property
+	     * that -LONG_MIN == LONG_MIN and a modulus with
+	     * a negative divisor is not well defined in C.
+	     */
+
+	    if ( i == LONG_MIN && i2 == -1 ) {
+		/* Integer overflow could happen with
+		 * (LONG_MIN % -1) even though it
+		 * is not possible in the code below. */
+		rem = 0;
+	    } else if ( i == LONG_MAX && i2 == LONG_MIN ) {
+		rem = -1;
+		neg_divisor = 1;
+	    } else if ( i == LONG_MIN && i2 == LONG_MIN ) {
+		rem = 0;
+	    } else if ( i == LONG_MIN && i2 == LONG_MAX ) {
+		rem = LONG_MAX - 1;
+	    } else if ( i2 == LONG_MIN ) {
+		/* i % LONG_MIN is not well defined since
+		 * the divisor would be a negative number
+		 * and -LONG_MIN is also a negative number.
+		 */
+		if (i == 0) {
+		    rem = 0;
+		} else if (i < 0) {
+		    rem = i;
+		} else {
+		    rem = LONG_MIN + i;
+		}
+	    } else {
+		if (i2 < 0) {
+		    i2 = -i2;
+		    i = -i; /* Note: -LONG_MIN == LONG_MIN */
+		    neg_divisor = 1;
+		}
+		rem = i % i2;
+
+		/*
+		 * remainder is (remainder + divisor) when the
+		 * remainder is negative. Watch out for the
+		 * special case of a LONG_MIN dividend and
+		 * a negative divisor. Don't add the divisor
+		 * in that case because the remainder should
+		 * not be negative.
+		 */
+		if (rem < 0 && !(neg_divisor && (i == LONG_MIN))) {
+		    rem += i2;
+		}
 	    }
-	    if (negative) {
+
+	    if ((neg_divisor && (rem > 0)) ||
+		    (!neg_divisor && (rem < 0))) {
 		rem = -rem;
 	    }
 	    iResult = rem;
@@ -3863,12 +3950,12 @@
 	 */
 
 	Tcl_ObjType *t1Ptr, *t2Ptr;
-	long i = 0, i2 = 0, quot, rem;	/* Init. avoids compiler warning. */
+	long i = 0, i2 = 0, quot;	/* Init. avoids compiler warning. */
 	double d1, d2;
 	long iResult = 0;	/* Init. avoids compiler warning. */
 	double dResult = 0.0;	/* Init. avoids compiler warning. */
 	int doDouble = 0;	/* 1 if doing floating arithmetic */
-	Tcl_WideInt w, w2, wquot, wrem;
+	Tcl_WideInt w, w2, wquot;
 	Tcl_WideInt wResult = W0; /* Init. avoids compiler warning. */
 	int doWide = 0;		/* 1 if doing wide arithmetic. */
 	Tcl_Obj *valuePtr,*value2Ptr;
@@ -4025,23 +4112,34 @@
 		    break;
 	        case INST_DIV:
 		    /*
-		     * This code is tricky: C doesn't guarantee much
-		     * about the quotient or remainder, but Tcl does.
-		     * The remainder always has the same sign as the
-		     * divisor and a smaller absolute value.
+		     * When performing integer division, protect
+		     * against integer overflow. Round towards zero
+		     * when the quotient is positive, otherwise
+		     * round towards -Infinity.
 		     */
 		    if (w2 == W0) {
 			TRACE((LLD" "LLD" => DIVIDE BY ZERO\n", w, w2));
 			goto divideByZero;
 		    }
-		    if (w2 < 0) {
-			w2 = -w2;
-			w = -w;
-		    }
-		    wquot = w / w2;
-		    wrem  = w % w2;
-		    if (wrem < W0) {
-			wquot -= 1;
+		    if (w == LLONG_MIN && w2 == -1) {
+			/* Avoid integer overflow on (LLONG_MIN / -1) */
+			wquot = LLONG_MIN;
+                    } else {
+			wquot = w / w2;
+			/* Round down to a smaller negative number if
+			 * there is a remainder and the quotient is
+			 * negative or zero and the signs don't match.
+			 * Note that we don't use a modulus to find the
+			 * remainder since it is not well defined in C
+			 * when the divisor is negative.
+			 */
+			if (((wquot < 0) ||
+			        ((wquot == 0) &&
+			            (((w < 0) && (w2 > 0)) ||
+			             ((w > 0) && (w2 < 0))))) &&
+			        ((wquot * w2) != w)) {
+			    wquot -= 1;
+			}
 		    }
 		    wResult = wquot;
 		    break;
@@ -4072,23 +4170,34 @@
 		    break;
 	        case INST_DIV:
 		    /*
-		     * This code is tricky: C doesn't guarantee much
-		     * about the quotient or remainder, but Tcl does.
-		     * The remainder always has the same sign as the
-		     * divisor and a smaller absolute value.
+		     * When performing integer division, protect
+		     * against integer overflow. Round towards zero
+		     * when the quotient is positive, otherwise
+		     * round towards -Infinity.
 		     */
 		    if (i2 == 0) {
 			TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2));
 			goto divideByZero;
 		    }
-		    if (i2 < 0) {
-			i2 = -i2;
-			i = -i;
-		    }
-		    quot = i / i2;
-		    rem  = i % i2;
-		    if (rem < 0) {
-			quot -= 1;
+		    if (i == LONG_MIN && i2 == -1) {
+			/* Avoid integer overflow on (LONG_MIN / -1) */
+			quot = LONG_MIN;
+		    } else {
+			quot = i / i2;
+			/* Round down to a smaller negative number if
+			 * there is a remainder and the quotient is
+			 * negative or zero and the signs don't match.
+			 * Note that we don't use a modulus to find the
+			 * remainder since it is not well defined in C
+			 * when the divisor is negative.
+			 */
+			if (((quot < 0) ||
+			        ((quot == 0) &&
+			            (((i < 0) && (i2 > 0)) ||
+			             ((i > 0) && (i2 < 0))))) &&
+			        ((quot * i2) != i)) {
+			    quot -= 1;
+			}
 		    }
 		    iResult = quot;
 		    break;
Index: tests/expr.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/expr.test,v
retrieving revision 1.34
diff -u -r1.34 expr.test
--- tests/expr.test	29 Jun 2005 03:29:02 -0000	1.34
+++ tests/expr.test	30 Jun 2005 07:49:05 -0000
@@ -5495,10 +5495,657 @@
 } {-9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808 1 1}
 
 
+set min -2147483648
+set max 2147483647
+
+test expr-34.1 {expr edge cases} {longis32bit} {
+    expr {$min / $min}
+} {1}
+
+test expr-34.2 {expr edge cases} {longis32bit} {
+    expr {$min % $min}
+} {0}
+
+test expr-34.3 {expr edge cases} {longis32bit} {
+    expr {$min / ($min + 1)}
+} {1}
+
+test expr-34.4 {expr edge cases} {longis32bit} {
+    expr {$min % ($min + 1)}
+} {-1}
+
+test expr-34.5 {expr edge cases} {longis32bit} {
+    expr {$min / ($min + 2)}
+} {1}
+
+test expr-34.6 {expr edge cases} {longis32bit} {
+    expr {$min % ($min + 2)}
+} {-2}
+
+test expr-34.7 {expr edge cases} {longis32bit} {
+    expr {$min / ($min + 3)}
+} {1}
+
+test expr-34.8 {expr edge cases} {longis32bit} {
+    expr {$min % ($min + 3)}
+} {-3}
+
+test expr-34.9 {expr edge cases} {longis32bit} {
+    expr {$min / -3}
+} {715827882}
+
+test expr-34.10 {expr edge cases} {longis32bit} {
+    expr {$min % -3}
+} {-2}
+
+test expr-34.11 {expr edge cases} {longis32bit} {
+    expr {$min / -2}
+} {1073741824}
+
+test expr-34.12 {expr edge cases} {longis32bit} {
+    expr {$min % -2}
+} {0}
+
+test expr-34.13 {expr edge cases} {longis32bit} {
+    expr {$min / -1}
+} {-2147483648}
+
+test expr-34.14 {expr edge cases} {longis32bit} {
+    expr {$min % -1}
+} {0}
+
+test expr-34.15 {expr edge cases} {longis32bit} {
+    expr {$min * -1}
+} {-2147483648}
+
+test expr-34.16 {expr edge cases} {longis32bit} {
+    expr {-$min}
+} {-2147483648}
+
+test expr-34.17 {expr edge cases} {longis32bit} {
+    expr {$min / 1}
+} {-2147483648}
+
+test expr-34.18 {expr edge cases} {longis32bit} {
+    expr {$min % 1}
+} {0}
+
+test expr-34.19 {expr edge cases} {longis32bit} {
+    expr {$min / 2}
+} {-1073741824}
+
+test expr-34.20 {expr edge cases} {longis32bit} {
+    expr {$min % 2}
+} {0}
+
+test expr-34.21 {expr edge cases} {longis32bit} {
+    expr {$min / 3}
+} {-715827883}
+
+test expr-34.22 {expr edge cases} {longis32bit} {
+    expr {$min % 3}
+} {1}
+
+test expr-34.23 {expr edge cases} {longis32bit} {
+    expr {$max / $min}
+} {-1}
+
+test expr-34.24 {expr edge cases} {longis32bit} {
+    expr {$max % $min}
+} {-1}
+
+test expr-34.25 {expr edge cases} {longis32bit} {
+    expr {$max / $max}
+} {1}
+
+test expr-34.26 {expr edge cases} {longis32bit} {
+    expr {$max % $max}
+} {0}
+
+test expr-34.27 {expr edge cases} {longis32bit} {
+    expr {$max / ($max - 1)}
+} {1}
+
+test expr-34.28 {expr edge cases} {longis32bit} {
+    expr {$max % ($max - 1)}
+} {1}
+
+test expr-34.29 {expr edge cases} {longis32bit} {
+    expr {$max / ($max - 2)}
+} {1}
+
+test expr-34.30 {expr edge cases} {longis32bit} {
+    expr {$max % ($max - 2)}
+} {2}
+
+test expr-34.31 {expr edge cases} {longis32bit} {
+    expr {$max / ($max - 3)}
+} {1}
+
+test expr-34.32 {expr edge cases} {longis32bit} {
+    expr {$max % ($max - 3)}
+} {3}
+
+test expr-34.33 {expr edge cases} {longis32bit} {
+    expr {$max / 3}
+} {715827882}
+
+test expr-34.34 {expr edge cases} {longis32bit} {
+    expr {$max % 3}
+} {1}
+
+test expr-34.35 {expr edge cases} {longis32bit} {
+    expr {$max / 2}
+} {1073741823}
+
+test expr-34.36 {expr edge cases} {longis32bit} {
+    expr {$max % 2}
+} {1}
+
+test expr-34.37 {expr edge cases} {longis32bit} {
+    expr {$max / 1}
+} {2147483647}
+
+test expr-34.38 {expr edge cases} {longis32bit} {
+    expr {$max % 1}
+} {0}
+
+test expr-34.39 {expr edge cases} {longis32bit} {
+    expr {$max / -1}
+} {-2147483647}
+
+test expr-34.40 {expr edge cases} {longis32bit} {
+    expr {$max % -1}
+} {0}
+
+test expr-34.41 {expr edge cases} {longis32bit} {
+    expr {$max / -2}
+} {-1073741824}
+
+test expr-34.42 {expr edge cases} {longis32bit} {
+    expr {$max % -2}
+} {-1}
+
+test expr-34.43 {expr edge cases} {longis32bit} {
+    expr {$max / -3}
+} {-715827883}
+
+test expr-34.44 {expr edge cases} {longis32bit} {
+    expr {$max % -3}
+} {-2}
+
+test expr-34.45 {expr edge cases} {longis32bit} {
+    expr {$min / $max}
+} {-2}
+
+test expr-34.46 {expr edge cases} {longis32bit} {
+    expr {$min % $max}
+} {2147483646}
+
+test expr-34.47 {expr edge cases} {longis32bit} {
+    expr {($min + 1) / ($max - 1)}
+} {-2}
+
+test expr-34.48 {expr edge cases} {longis32bit} {
+    expr {($min + 1) % ($max - 1)}
+} {2147483645}
+
+test expr-34.49 {expr edge cases} {longis32bit} {
+    expr {($max - 1) / ($min + 1)}
+} {-1}
+
+test expr-34.50 {expr edge cases} {longis32bit} {
+    expr {($max - 1) % ($min + 1)}
+} {-1}
+
+# Euclidean property:
+# quotient * divisor + remainder = dividend
+
+test expr-34.51 {expr edge cases} {longis32bit} {
+    set dividend $max
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($divisor * $q) + $r}]
+} {1073741823 * 2 + 1 = 2147483647}
+
+test expr-34.52 {expr edge cases} {longis32bit} {
+    set dividend [expr {$max - 1}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1073741823 * 2 + 0 = 2147483646}
+
+test expr-34.53 {expr edge cases} {longis32bit} {
+    set dividend [expr {$max - 2}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1073741822 * 2 + 1 = 2147483645}
+
+test expr-34.54 {expr edge cases} {longis32bit} {
+    set dividend $max
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {715827882 * 3 + 1 = 2147483647}
+
+test expr-34.55 {expr edge cases} {longis32bit} {
+    set dividend [expr {$max - 1}]
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {715827882 * 3 + 0 = 2147483646}
+
+test expr-34.56 {expr edge cases} {longis32bit} {
+    set dividend [expr {$max - 2}]
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {715827881 * 3 + 2 = 2147483645}
+
+test expr-34.57 {expr edge cases} {longis32bit} {
+    set dividend $min
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-1073741824 * 2 + 0 = -2147483648}
+
+test expr-34.58 {expr edge cases} {longis32bit} {
+    set dividend [expr {$min + 1}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-1073741824 * 2 + 1 = -2147483647}
+
+test expr-34.59 {expr edge cases} {longis32bit} {
+    set dividend [expr {$min + 2}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-1073741823 * 2 + 0 = -2147483646}
+
+test expr-34.60 {expr edge cases} {longis32bit} {
+    # Two things could happen here. The multiplication
+    # could overflow a 32 bit type, so that when
+    # 1 is added it overflows again back to min.
+    # The multiplication could also use a wide type
+    # to hold ($min - 1) until 1 is added and
+    # the number becomes $min again.
+    set dividend $min
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-715827883 * 3 + 1 = -2147483648}
+
+test expr-34.61 {expr edge cases} {longis32bit} {
+    set dividend $min
+    set divisor -3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {715827882 * -3 + -2 = -2147483648}
+
+test expr-34.62 {expr edge cases} {longis32bit} {
+    set dividend $min
+    set divisor $min
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1 * -2147483648 + 0 = -2147483648}
+
+test expr-34.63 {expr edge cases} {longis32bit} {
+    set dividend $min
+    set divisor [expr {$min + 1}]
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1 * -2147483647 + -1 = -2147483648}
+
+test expr-34.64 {expr edge cases} {longis32bit} {
+    set dividend $min
+    set divisor [expr {$min + 2}]
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1 * -2147483646 + -2 = -2147483648}
+
+# 64bit wide integer range checks
+
+set min -9223372036854775808
+set max 9223372036854775807
+
+test expr-35.1 {expr edge cases} {wideis64bit} {
+    expr {$min / $min}
+} {1}
+
+test expr-35.2 {expr edge cases} {wideis64bit} {
+    expr {$min % $min}
+} {0}
+
+test expr-35.3 {expr edge cases} {wideis64bit} {
+    expr {$min / ($min + 1)}
+} {1}
+
+test expr-35.4 {expr edge cases} {wideis64bit} {
+    expr {$min % ($min + 1)}
+} {-1}
+
+test expr-35.5 {expr edge cases} {wideis64bit} {
+    expr {$min / ($min + 2)}
+} {1}
+
+test expr-35.6 {expr edge cases} {wideis64bit} {
+    expr {$min % ($min + 2)}
+} {-2}
+
+test expr-35.7 {expr edge cases} {wideis64bit} {
+    expr {$min / ($min + 3)}
+} {1}
+
+test expr-35.8 {expr edge cases} {wideis64bit} {
+    expr {$min % ($min + 3)}
+} {-3}
+
+test expr-35.9 {expr edge cases} {wideis64bit} {
+    expr {$min / -3}
+} {3074457345618258602}
+
+test expr-35.10 {expr edge cases} {wideis64bit} {
+    expr {$min % -3}
+} {-2}
+
+test expr-35.11 {expr edge cases} {wideis64bit} {
+    expr {$min / -2}
+} {4611686018427387904}
+
+test expr-35.12 {expr edge cases} {wideis64bit} {
+    expr {$min % -2}
+} {0}
+
+test expr-35.13 {expr edge cases} {wideis64bit} {
+    expr {$min / -1}
+} {-9223372036854775808}
+
+test expr-35.14 {expr edge cases} {wideis64bit} {
+    expr {$min % -1}
+} {0}
+
+test expr-35.15 {expr edge cases} {wideis64bit} {
+    expr {$min * -1}
+} {-9223372036854775808}
+
+test expr-35.16 {expr edge cases} {wideis64bit} {
+    expr {-$min}
+} {-9223372036854775808}
+
+test expr-35.17 {expr edge cases} {wideis64bit} {
+    expr {$min / 1}
+} {-9223372036854775808}
+
+test expr-35.18 {expr edge cases} {wideis64bit} {
+    expr {$min % 1}
+} {0}
+
+test expr-35.19 {expr edge cases} {wideis64bit} {
+    expr {$min / 2}
+} {-4611686018427387904}
+
+test expr-35.20 {expr edge cases} {wideis64bit} {
+    expr {$min % 2}
+} {0}
+
+test expr-35.21 {expr edge cases} {wideis64bit} {
+    expr {$min / 3}
+} {-3074457345618258603}
+
+test expr-35.22 {expr edge cases} {wideis64bit} {
+    expr {$min % 3}
+} {1}
+
+test expr-35.23 {expr edge cases} {wideis64bit} {
+    expr {$max / $min}
+} {-1}
+
+test expr-35.24 {expr edge cases} {wideis64bit} {
+    expr {$max % $min}
+} {-1}
+
+test expr-35.25 {expr edge cases} {wideis64bit} {
+    expr {$max / $max}
+} {1}
+
+test expr-35.26 {expr edge cases} {wideis64bit} {
+    expr {$max % $max}
+} {0}
+
+test expr-35.27 {expr edge cases} {wideis64bit} {
+    expr {$max / ($max - 1)}
+} {1}
+
+test expr-35.28 {expr edge cases} {wideis64bit} {
+    expr {$max % ($max - 1)}
+} {1}
+
+test expr-35.29 {expr edge cases} {wideis64bit} {
+    expr {$max / ($max - 2)}
+} {1}
+
+test expr-35.30 {expr edge cases} {wideis64bit} {
+    expr {$max % ($max - 2)}
+} {2}
+
+test expr-35.31 {expr edge cases} {wideis64bit} {
+    expr {$max / ($max - 3)}
+} {1}
+
+test expr-35.32 {expr edge cases} {wideis64bit} {
+    expr {$max % ($max - 3)}
+} {3}
+
+test expr-35.33 {expr edge cases} {wideis64bit} {
+    expr {$max / 3}
+} {3074457345618258602}
+
+test expr-35.34 {expr edge cases} {wideis64bit} {
+    expr {$max % 3}
+} {1}
+
+test expr-35.35 {expr edge cases} {wideis64bit} {
+    expr {$max / 2}
+} {4611686018427387903}
+
+test expr-35.36 {expr edge cases} {wideis64bit} {
+    expr {$max % 2}
+} {1}
+
+test expr-35.37 {expr edge cases} {wideis64bit} {
+    expr {$max / 1}
+} {9223372036854775807}
+
+test expr-35.38 {expr edge cases} {wideis64bit} {
+    expr {$max % 1}
+} {0}
+
+test expr-35.39 {expr edge cases} {wideis64bit} {
+    expr {$max / -1}
+} {-9223372036854775807}
+
+test expr-35.40 {expr edge cases} {wideis64bit} {
+    expr {$max % -1}
+} {0}
+
+test expr-35.41 {expr edge cases} {wideis64bit} {
+    expr {$max / -2}
+} {-4611686018427387904}
+
+test expr-35.42 {expr edge cases} {wideis64bit} {
+    expr {$max % -2}
+} {-1}
+
+test expr-35.43 {expr edge cases} {wideis64bit} {
+    expr {$max / -3}
+} {-3074457345618258603}
+
+test expr-35.44 {expr edge cases} {wideis64bit} {
+    expr {$max % -3}
+} {-2}
+
+test expr-35.45 {expr edge cases} {wideis64bit} {
+    expr {$min / $max}
+} {-2}
+
+test expr-35.46 {expr edge cases} {wideis64bit} {
+    expr {$min % $max}
+} {9223372036854775806}
+
+test expr-35.47 {expr edge cases} {wideis64bit} {
+    expr {($min + 1) / ($max - 1)}
+} {-2}
+
+test expr-35.48 {expr edge cases} {wideis64bit} {
+    expr {($min + 1) % ($max - 1)}
+} {9223372036854775805}
+
+test expr-35.49 {expr edge cases} {wideis64bit} {
+    expr {($max - 1) / ($min + 1)}
+} {-1}
+
+test expr-35.50 {expr edge cases} {wideis64bit} {
+    expr {($max - 1) % ($min + 1)}
+} {-1}
+
+test expr-35.51 {expr edge cases} {wideis64bit} {
+    set dividend $max
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($divisor * $q) + $r}]
+} {4611686018427387903 * 2 + 1 = 9223372036854775807}
+
+test expr-35.52 {expr edge cases} {wideis64bit} {
+    set dividend [expr {$max - 1}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {4611686018427387903 * 2 + 0 = 9223372036854775806}
+
+test expr-35.53 {expr edge cases} {wideis64bit} {
+    set dividend [expr {$max - 2}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {4611686018427387902 * 2 + 1 = 9223372036854775805}
+
+test expr-35.54 {expr edge cases} {wideis64bit} {
+    set dividend $max
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {3074457345618258602 * 3 + 1 = 9223372036854775807}
+
+test expr-35.55 {expr edge cases} {wideis64bit} {
+    set dividend [expr {$max - 1}]
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {3074457345618258602 * 3 + 0 = 9223372036854775806}
+
+test expr-35.56 {expr edge cases} {wideis64bit} {
+    set dividend [expr {$max - 2}]
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {3074457345618258601 * 3 + 2 = 9223372036854775805}
+
+test expr-35.57 {expr edge cases} {wideis64bit} {
+    set dividend $min
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-4611686018427387904 * 2 + 0 = -9223372036854775808}
+
+test expr-35.58 {expr edge cases} {wideis64bit} {
+    set dividend [expr {$min + 1}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-4611686018427387904 * 2 + 1 = -9223372036854775807}
+
+test expr-35.59 {expr edge cases} {wideis64bit} {
+    set dividend [expr {$min + 2}]
+    set divisor 2
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-4611686018427387903 * 2 + 0 = -9223372036854775806}
+
+test expr-35.60 {expr edge cases} {wideis64bit} {
+    # Multiplication overflows 64 bit type here,
+    # so when the 1 is added it overflows
+    # again and we end up back at min.
+    set dividend $min
+    set divisor 3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {-3074457345618258603 * 3 + 1 = -9223372036854775808}
+
+test expr-35.61 {expr edge cases} {wideis64bit} {
+    set dividend $min
+    set divisor -3
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {3074457345618258602 * -3 + -2 = -9223372036854775808}
+
+test expr-35.62 {expr edge cases} {wideis64bit} {
+    set dividend $min
+    set divisor $min
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1 * -9223372036854775808 + 0 = -9223372036854775808}
+
+test expr-35.63 {expr edge cases} {wideis64bit} {
+    set dividend $min
+    set divisor [expr {$min + 1}]
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1 * -9223372036854775807 + -1 = -9223372036854775808}
+
+test expr-35.64 {expr edge cases} {wideis64bit} {
+    set dividend $min
+    set divisor [expr {$min + 2}]
+    set q [expr {$dividend / $divisor}]
+    set r [expr {$dividend % $divisor}]
+    list $q * $divisor + $r = [expr {($q * $divisor) + $r}]
+} {1 * -9223372036854775806 + -2 = -9223372036854775808}
+
+
 # cleanup
 if {[info exists a]} {
     unset a
 }
+catch {unset min}
+catch {unset max}
 ::tcltest::cleanupTests
 return