diff options
author | mdejong <mdejong> | 2005-07-09 00:27:25 (GMT) |
---|---|---|
committer | mdejong <mdejong> | 2005-07-09 00:27:25 (GMT) |
commit | eef683116916bd916b5d804a98110b9e7139dcc2 (patch) | |
tree | acabb9156822cf78785eb37708fce262e826f50d | |
parent | 199c02aeae93a951a592d2c5f1a9b336a891cc21 (diff) | |
download | tcl-eef683116916bd916b5d804a98110b9e7139dcc2.zip tcl-eef683116916bd916b5d804a98110b9e7139dcc2.tar.gz tcl-eef683116916bd916b5d804a98110b9e7139dcc2.tar.bz2 |
* 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]
-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 |