diff options
| -rw-r--r-- | ChangeLog | 18 | ||||
| -rw-r--r-- | generic/tclExecute.c | 190 | ||||
| -rw-r--r-- | tests/expr.test | 970 | 
3 files changed, 1128 insertions, 50 deletions
| @@ -1,3 +1,21 @@ +2005-07-08  Mo DeJong  <mdejong@users.sourceforge.net> + +	* 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. +	[Patch 1230205] +  2005-07-06  Don Porter  <dgp@users.sourceforge.net>  	* generic/tclLink.c:	Simplified LinkTraceProc [Bug 1208108]. diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 06456d3..90ee259 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -11,7 +11,7 @@   * See the file "license.terms" for information on usage and redistribution   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * - * RCS: @(#) $Id: tclExecute.c,v 1.193 2005/06/29 03:29:00 mdejong Exp $ + * RCS: @(#) $Id: tclExecute.c,v 1.194 2005/07/09 00:27:32 mdejong Exp $   */  #include "tclInt.h" @@ -3556,7 +3556,7 @@ TclExecuteByteCode(interp, codePtr)  	 * 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 @@ TclExecuteByteCode(interp, codePtr)  	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 @@ TclExecuteByteCode(interp, codePtr)  		}  		goto divideByZero;  	    } -	    negative = 0;  	    if (valuePtr->typePtr == &tclWideIntType  		|| value2Ptr->typePtr == &tclWideIntType) {  		Tcl_WideInt wRemainder; @@ -3631,32 +3631,102 @@ TclExecuteByteCode(interp, codePtr)  		} 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; +		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_MIN && w2 == LLONG_MAX ) { +		    wRemainder = LLONG_MAX - 1; +		} else if ( w2 == LLONG_MIN ) { +		    /* In C, a modulus operation is not well +		     * defined when the divisor is a negative +		     * number. So w % LLONG_MIN is not well +		     * defined in the code below because +		     * -LLONG_MIN is still a negative number. +		     */ +		    if (w == 0 || w == LLONG_MIN) { +		        wRemainder = 0; +		    } else if (w < 0) { +		        wRemainder = w; +		    } else { +		        wRemainder = LLONG_MIN + w; +		    } +		    neg_divisor = 1; +		} 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; + +	    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_MIN && i2 == LONG_MAX ) { +		rem = LONG_MAX - 1; +	    } else if ( i2 == LONG_MIN ) { +		/* In C, a modulus operation is not well +		 * defined when the divisor is a negative +		 * number. So i % LONG_MIN is not well +		 * defined in the code below because +		 * -LONG_MIN is still a negative number. +		 */ +		if (i == 0 || i == LONG_MIN) { +		    rem = 0; +		} else if (i < 0) { +		    rem = i; +		} else { +		    rem = LONG_MIN + i; +		} +		neg_divisor = 1; +	    } 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 +3933,12 @@ TclExecuteByteCode(interp, codePtr)  	 */  	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 +4095,34 @@ TclExecuteByteCode(interp, codePtr)  		    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 +4153,34 @@ TclExecuteByteCode(interp, codePtr)  		    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; diff --git a/tests/expr.test b/tests/expr.test index a7ea152..781eb8e 100644 --- a/tests/expr.test +++ b/tests/expr.test @@ -10,7 +10,7 @@  # See the file "license.terms" for information on usage and redistribution  # of this file, and for a DISCLAIMER OF ALL WARRANTIES.  # -# RCS: @(#) $Id: expr.test,v 1.34 2005/06/29 03:29:02 mdejong Exp $ +# RCS: @(#) $Id: expr.test,v 1.35 2005/07/09 00:27:33 mdejong Exp $  if {[lsearch [namespace children] ::tcltest] == -1} {      package require tcltest 2.1 @@ -5495,10 +5495,978 @@ test expr-33.4 {parse smallest wide value} {wideis64bit} {  } {-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} +} $min + +test expr-34.16 {expr edge cases} {longis32bit} { +    expr {-$min} +} $min + +test expr-34.17 {expr edge cases} {longis32bit} { +    expr {$min / 1} +} $min + +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 {$min / ($max - 3)} +} {-2} + +test expr-34.24 {expr edge cases} {longis32bit} { +    expr {$min % ($max - 3)} +} {2147483640} + +test expr-34.25 {expr edge cases} {longis32bit} { +    expr {$min / ($max - 2)} +} {-2} + +test expr-34.26 {expr edge cases} {longis32bit} { +    expr {$min % ($max - 2)} +} {2147483642} + +test expr-34.27 {expr edge cases} {longis32bit} { +    expr {$min / ($max - 1)} +} {-2} + +test expr-34.28 {expr edge cases} {longis32bit} { +    expr {$min % ($max - 1)} +} {2147483644} + +test expr-34.29 {expr edge cases} {longis32bit} { +    expr {$min / $max} +} {-2} + +test expr-34.30 {expr edge cases} {longis32bit} { +    expr {$min % $max} +} {2147483646} + +test expr-34.31 {expr edge cases} {longis32bit} { +    expr {$max / $max} +} {1} + +test expr-34.32 {expr edge cases} {longis32bit} { +    expr {$max % $max} +} {0} + +test expr-34.33 {expr edge cases} {longis32bit} { +    expr {$max / ($max - 1)} +} {1} + +test expr-34.34 {expr edge cases} {longis32bit} { +    expr {$max % ($max - 1)} +} {1} + +test expr-34.35 {expr edge cases} {longis32bit} { +    expr {$max / ($max - 2)} +} {1} + +test expr-34.36 {expr edge cases} {longis32bit} { +    expr {$max % ($max - 2)} +} {2} + +test expr-34.37 {expr edge cases} {longis32bit} { +    expr {$max / ($max - 3)} +} {1} + +test expr-34.38 {expr edge cases} {longis32bit} { +    expr {$max % ($max - 3)} +} {3} + +test expr-34.39 {expr edge cases} {longis32bit} { +    expr {$max / 3} +} {715827882} + +test expr-34.40 {expr edge cases} {longis32bit} { +    expr {$max % 3} +} {1} + +test expr-34.41 {expr edge cases} {longis32bit} { +    expr {$max / 2} +} {1073741823} + +test expr-34.42 {expr edge cases} {longis32bit} { +    expr {$max % 2} +} {1} + +test expr-34.43 {expr edge cases} {longis32bit} { +    expr {$max / 1} +} $max + +test expr-34.44 {expr edge cases} {longis32bit} { +    expr {$max % 1} +} {0} + +test expr-34.45 {expr edge cases} {longis32bit} { +    expr {$max / -1} +} "-$max" + +test expr-34.46 {expr edge cases} {longis32bit} { +    expr {$max % -1} +} {0} + +test expr-34.47 {expr edge cases} {longis32bit} { +    expr {$max / -2} +} {-1073741824} + +test expr-34.48 {expr edge cases} {longis32bit} { +    expr {$max % -2} +} {-1} + +test expr-34.49 {expr edge cases} {longis32bit} { +    expr {$max / -3} +} {-715827883} + +test expr-34.50 {expr edge cases} {longis32bit} { +    expr {$max % -3} +} {-2} + +test expr-34.51 {expr edge cases} {longis32bit} { +    expr {$max / ($min + 3)} +} {-2} + +test expr-34.52 {expr edge cases} {longis32bit} { +    expr {$max % ($min + 3)} +} {-2147483643} + +test expr-34.53 {expr edge cases} {longis32bit} { +    expr {$max / ($min + 2)} +} {-2} + +test expr-34.54 {expr edge cases} {longis32bit} { +    expr {$max % ($min + 2)} +} {-2147483645} + +test expr-34.55 {expr edge cases} {longis32bit} { +    expr {$max / ($min + 1)} +} {-1} + +test expr-34.56 {expr edge cases} {longis32bit} { +    expr {$max % ($min + 1)} +} {0} + +test expr-34.57 {expr edge cases} {longis32bit} { +    expr {$max / $min} +} {-1} + +test expr-34.58 {expr edge cases} {longis32bit} { +    expr {$max % $min} +} {-1} + +test expr-34.59 {expr edge cases} {longis32bit} { +    expr {($min + 1) / ($max - 1)} +} {-2} + +test expr-34.60 {expr edge cases} {longis32bit} { +    expr {($min + 1) % ($max - 1)} +} {2147483645} + +test expr-34.61 {expr edge cases} {longis32bit} { +    expr {($max - 1) / ($min + 1)} +} {-1} + +test expr-34.62 {expr edge cases} {longis32bit} { +    expr {($max - 1) % ($min + 1)} +} {-1} + +test expr-34.63 {expr edge cases} {longis32bit} { +    expr {($max - 1) / $min} +} {-1} + +test expr-34.64 {expr edge cases} {longis32bit} { +    expr {($max - 1) % $min} +} {-2} + +test expr-34.65 {expr edge cases} {longis32bit} { +    expr {($max - 2) / $min} +} {-1} + +test expr-34.66 {expr edge cases} {longis32bit} { +    expr {($max - 2) % $min} +} {-3} + +test expr-34.67 {expr edge cases} {longis32bit} { +    expr {($max - 3) / $min} +} {-1} + +test expr-34.68 {expr edge cases} {longis32bit} { +    expr {($max - 3) % $min} +} {-4} + +test expr-34.69 {expr edge cases} {longis32bit} { +    expr {-3 / $min} +} {0} + +test expr-34.70 {expr edge cases} {longis32bit} { +    expr {-3 % $min} +} {-3} + +test expr-34.71 {expr edge cases} {longis32bit} { +    expr {-2 / $min} +} {0} + +test expr-34.72 {expr edge cases} {longis32bit} { +    expr {-2 % $min} +} {-2} + +test expr-34.73 {expr edge cases} {longis32bit} { +    expr {-1 / $min} +} {0} + +test expr-34.74 {expr edge cases} {longis32bit} { +    expr {-1 % $min} +} {-1} + +test expr-34.75 {expr edge cases} {longis32bit} { +    expr {0 / $min} +} {0} + +test expr-34.76 {expr edge cases} {longis32bit} { +    expr {0 % $min} +} {0} + +test expr-34.77 {expr edge cases} {longis32bit} { +    expr {0 / ($min + 1)} +} {0} + +test expr-34.78 {expr edge cases} {longis32bit} { +    expr {0 % ($min + 1)} +} {0} + +test expr-34.79 {expr edge cases} {longis32bit} { +    expr {1 / $min} +} {-1} + +test expr-34.80 {expr edge cases} {longis32bit} { +    expr {1 % $min} +} {-2147483647} + +test expr-34.81 {expr edge cases} {longis32bit} { +    expr {1 / ($min + 1)} +} {-1} + +test expr-34.82 {expr edge cases} {longis32bit} { +    expr {1 % ($min + 1)} +} {-2147483646} + +test expr-34.83 {expr edge cases} {longis32bit} { +    expr {2 / $min} +} {-1} + +test expr-34.84 {expr edge cases} {longis32bit} { +    expr {2 % $min} +} {-2147483646} + +test expr-34.85 {expr edge cases} {longis32bit} { +    expr {2 / ($min + 1)} +} {-1} + +test expr-34.86 {expr edge cases} {longis32bit} { +    expr {2 % ($min + 1)} +} {-2147483645} + +test expr-34.87 {expr edge cases} {longis32bit} { +    expr {3 / $min} +} {-1} + +test expr-34.88 {expr edge cases} {longis32bit} { +    expr {3 % $min} +} {-2147483645} + +test expr-34.89 {expr edge cases} {longis32bit} { +    expr {3 / ($min + 1)} +} {-1} + +test expr-34.90 {expr edge cases} {longis32bit} { +    expr {3 % ($min + 1)} +} {-2147483644} + +# Euclidean property: +# quotient * divisor + remainder = dividend + +test expr-35.1 {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-35.2 {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-35.3 {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-35.4 {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-35.5 {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-35.6 {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-35.7 {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-35.8 {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-35.9 {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-35.10 {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-35.11 {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-35.12 {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-35.13 {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-35.14 {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 checks + +set min -9223372036854775808 +set max 9223372036854775807 + +test expr-36.1 {expr edge cases} {wideis64bit} { +    expr {$min / $min} +} {1} + +test expr-36.2 {expr edge cases} {wideis64bit} { +    expr {$min % $min} +} {0} + +test expr-36.3 {expr edge cases} {wideis64bit} { +    expr {$min / ($min + 1)} +} {1} + +test expr-36.4 {expr edge cases} {wideis64bit} { +    expr {$min % ($min + 1)} +} {-1} + +test expr-36.5 {expr edge cases} {wideis64bit} { +    expr {$min / ($min + 2)} +} {1} + +test expr-36.6 {expr edge cases} {wideis64bit} { +    expr {$min % ($min + 2)} +} {-2} + +test expr-36.7 {expr edge cases} {wideis64bit} { +    expr {$min / ($min + 3)} +} {1} + +test expr-36.8 {expr edge cases} {wideis64bit} { +    expr {$min % ($min + 3)} +} {-3} + +test expr-36.9 {expr edge cases} {wideis64bit} { +    expr {$min / -3} +} {3074457345618258602} + +test expr-36.10 {expr edge cases} {wideis64bit} { +    expr {$min % -3} +} {-2} + +test expr-36.11 {expr edge cases} {wideis64bit} { +    expr {$min / -2} +} {4611686018427387904} + +test expr-36.12 {expr edge cases} {wideis64bit} { +    expr {$min % -2} +} {0} + +test expr-36.13 {expr edge cases} {wideis64bit} { +    expr {$min / -1} +} $min + +test expr-36.14 {expr edge cases} {wideis64bit} { +    expr {$min % -1} +} {0} + +test expr-36.15 {expr edge cases} {wideis64bit} { +    expr {$min * -1} +} $min + +test expr-36.16 {expr edge cases} {wideis64bit} { +    expr {-$min} +} $min + +test expr-36.17 {expr edge cases} {wideis64bit} { +    expr {$min / 1} +} $min + +test expr-36.18 {expr edge cases} {wideis64bit} { +    expr {$min % 1} +} {0} + +test expr-36.19 {expr edge cases} {wideis64bit} { +    expr {$min / 2} +} {-4611686018427387904} + +test expr-36.20 {expr edge cases} {wideis64bit} { +    expr {$min % 2} +} {0} + +test expr-36.21 {expr edge cases} {wideis64bit} { +    expr {$min / 3} +} {-3074457345618258603} + +test expr-36.22 {expr edge cases} {wideis64bit} { +    expr {$min % 3} +} {1} + +test expr-36.23 {expr edge cases} {wideis64bit} { +    expr {$min / ($max - 3)} +} {-2} + +test expr-36.24 {expr edge cases} {wideis64bit} { +    expr {$min % ($max - 3)} +} {9223372036854775800} + +test expr-36.25 {expr edge cases} {wideis64bit} { +    expr {$min / ($max - 2)} +} {-2} + +test expr-36.26 {expr edge cases} {wideis64bit} { +    expr {$min % ($max - 2)} +} {9223372036854775802} + +test expr-36.27 {expr edge cases} {wideis64bit} { +    expr {$min / ($max - 1)} +} {-2} + +test expr-36.28 {expr edge cases} {wideis64bit} { +    expr {$min % ($max - 1)} +} {9223372036854775804} + +test expr-36.29 {expr edge cases} {wideis64bit} { +    expr {$min / $max} +} {-2} + +test expr-36.30 {expr edge cases} {wideis64bit} { +    expr {$min % $max} +} {9223372036854775806} + +test expr-36.31 {expr edge cases} {wideis64bit} { +    expr {$max / $max} +} {1} + +test expr-36.32 {expr edge cases} {wideis64bit} { +    expr {$max % $max} +} {0} + +test expr-36.33 {expr edge cases} {wideis64bit} { +    expr {$max / ($max - 1)} +} {1} + +test expr-36.34 {expr edge cases} {wideis64bit} { +    expr {$max % ($max - 1)} +} {1} + +test expr-36.35 {expr edge cases} {wideis64bit} { +    expr {$max / ($max - 2)} +} {1} + +test expr-36.36 {expr edge cases} {wideis64bit} { +    expr {$max % ($max - 2)} +} {2} + +test expr-36.37 {expr edge cases} {wideis64bit} { +    expr {$max / ($max - 3)} +} {1} + +test expr-36.38 {expr edge cases} {wideis64bit} { +    expr {$max % ($max - 3)} +} {3} + +test expr-36.39 {expr edge cases} {wideis64bit} { +    expr {$max / 3} +} {3074457345618258602} + +test expr-36.40 {expr edge cases} {wideis64bit} { +    expr {$max % 3} +} {1} + +test expr-36.41 {expr edge cases} {wideis64bit} { +    expr {$max / 2} +} {4611686018427387903} + +test expr-36.42 {expr edge cases} {wideis64bit} { +    expr {$max % 2} +} {1} + +test expr-36.43 {expr edge cases} {wideis64bit} { +    expr {$max / 1} +} $max + +test expr-36.44 {expr edge cases} {wideis64bit} { +    expr {$max % 1} +} {0} + +test expr-36.45 {expr edge cases} {wideis64bit} { +    expr {$max / -1} +} "-$max" + +test expr-36.46 {expr edge cases} {wideis64bit} { +    expr {$max % -1} +} {0} + +test expr-36.47 {expr edge cases} {wideis64bit} { +    expr {$max / -2} +} {-4611686018427387904} + +test expr-36.48 {expr edge cases} {wideis64bit} { +    expr {$max % -2} +} {-1} + +test expr-36.49 {expr edge cases} {wideis64bit} { +    expr {$max / -3} +} {-3074457345618258603} + +test expr-36.50 {expr edge cases} {wideis64bit} { +    expr {$max % -3} +} {-2} + +test expr-36.51 {expr edge cases} {wideis64bit} { +    expr {$max / ($min + 3)} +} {-2} + +test expr-36.52 {expr edge cases} {wideis64bit} { +    expr {$max % ($min + 3)} +} {-9223372036854775803} + +test expr-36.53 {expr edge cases} {wideis64bit} { +    expr {$max / ($min + 2)} +} {-2} + +test expr-36.54 {expr edge cases} {wideis64bit} { +    expr {$max % ($min + 2)} +} {-9223372036854775805} + +test expr-36.55 {expr edge cases} {wideis64bit} { +    expr {$max / ($min + 1)} +} {-1} + +test expr-36.56 {expr edge cases} {wideis64bit} { +    expr {$max % ($min + 1)} +} {0} + +test expr-36.57 {expr edge cases} {wideis64bit} { +    expr {$max / $min} +} {-1} + +test expr-36.58 {expr edge cases} {wideis64bit} { +    expr {$max % $min} +} {-1} + +test expr-36.59 {expr edge cases} {wideis64bit} { +    expr {($min + 1) / ($max - 1)} +} {-2} + +test expr-36.60 {expr edge cases} {wideis64bit} { +    expr {($min + 1) % ($max - 1)} +} {9223372036854775805} + +test expr-36.61 {expr edge cases} {wideis64bit} { +    expr {($max - 1) / ($min + 1)} +} {-1} + +test expr-36.62 {expr edge cases} {wideis64bit} { +    expr {($max - 1) % ($min + 1)} +} {-1} + +test expr-36.63 {expr edge cases} {wideis64bit} { +    expr {($max - 1) / $min} +} {-1} + +test expr-36.64 {expr edge cases} {wideis64bit} { +    expr {($max - 1) % $min} +} {-2} + +test expr-36.65 {expr edge cases} {wideis64bit} { +    expr {($max - 2) / $min} +} {-1} + +test expr-36.66 {expr edge cases} {wideis64bit} { +    expr {($max - 2) % $min} +} {-3} + +test expr-36.67 {expr edge cases} {wideis64bit} { +    expr {($max - 3) / $min} +} {-1} + +test expr-36.68 {expr edge cases} {wideis64bit} { +    expr {($max - 3) % $min} +} {-4} + +test expr-36.69 {expr edge cases} {wideis64bit} { +    expr {-3 / $min} +} {0} + +test expr-36.70 {expr edge cases} {wideis64bit} { +    expr {-3 % $min} +} {-3} + +test expr-36.71 {expr edge cases} {wideis64bit} { +    expr {-2 / $min} +} {0} + +test expr-36.72 {expr edge cases} {wideis64bit} { +    expr {-2 % $min} +} {-2} + +test expr-36.73 {expr edge cases} {wideis64bit} { +    expr {-1 / $min} +} {0} + +test expr-36.74 {expr edge cases} {wideis64bit} { +    expr {-1 % $min} +} {-1} + +test expr-36.75 {expr edge cases} {wideis64bit} { +    expr {0 / $min} +} {0} + +test expr-36.76 {expr edge cases} {wideis64bit} { +    expr {0 % $min} +} {0} + +test expr-36.77 {expr edge cases} {wideis64bit} { +    expr {0 / ($min + 1)} +} {0} + +test expr-36.78 {expr edge cases} {wideis64bit} { +    expr {0 % ($min + 1)} +} {0} + +test expr-36.79 {expr edge cases} {wideis64bit} { +    expr {1 / $min} +} {-1} + +test expr-36.80 {expr edge cases} {wideis64bit} { +    expr {1 % $min} +} {-9223372036854775807} + +test expr-36.81 {expr edge cases} {wideis64bit} { +    expr {1 / ($min + 1)} +} {-1} + +test expr-36.82 {expr edge cases} {wideis64bit} { +    expr {1 % ($min + 1)} +} {-9223372036854775806} + +test expr-36.83 {expr edge cases} {wideis64bit} { +    expr {2 / $min} +} {-1} + +test expr-36.84 {expr edge cases} {wideis64bit} { +    expr {2 % $min} +} {-9223372036854775806} + +test expr-36.85 {expr edge cases} {wideis64bit} { +    expr {2 / ($min + 1)} +} {-1} + +test expr-36.86 {expr edge cases} {wideis64bit} { +    expr {2 % ($min + 1)} +} {-9223372036854775805} + +test expr-36.87 {expr edge cases} {wideis64bit} { +    expr {3 / $min} +} {-1} + +test expr-36.88 {expr edge cases} {wideis64bit} { +    expr {3 % $min} +} {-9223372036854775805} + +test expr-36.89 {expr edge cases} {wideis64bit} { +    expr {3 / ($min + 1)} +} {-1} + +test expr-36.90 {expr edge cases} {wideis64bit} { +    expr {3 % ($min + 1)} +} {-9223372036854775804} + + +test expr-37.1 {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-37.2 {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-37.3 {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-37.4 {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-37.5 {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-37.6 {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-37.7 {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-37.8 {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-37.9 {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-37.10 {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-37.11 {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-37.12 {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-37.13 {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-37.14 {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 | 
