[Richard Suchenwirth] - Mathematics with expr has some problems coming from the C implementation. On my NT box, and on most present-day computers, int is represented as a 32-bit signed "long". This means there is a limit, a.k.a. MAXINT, to what an integer can express: % expr 2147483647 2147483647 % expr 2147483648 -2147483648 Probably not what was intended. ([Larry Virden] interjects - I would much prefer to see Tcl do things that are 'intended' than not - if anyone else has this desire, perhaps it would be worthwhile to consider what compatible changes could be made to Tcl so that one gets the math one expects...) From the given threshold, positives are recast to negatives, up to % expr 4294967295 -1 and then you run into % expr 4294967296 error: integer value too large to represent You might consider using the multiple-precision extension called '''Mpexpr''' [http://www.nyx.net/~tpoindex/tcl.html], which can handle integer and floating point numbers with large numbers of digits, or the pure-Tcl [Arbitrary Precision Math Procedures]. As an alternative, you gain some more computing power by converting such strings to floating-point variables. You gain the advantage of a wider range of values and (usually) more significant digits, but you do not have unlimited precision. (You can't count all the way to 10e300 by ones.) You also lose the ability to use the '''[[incr]]''' command, but you can always use '''[[expr]]'''. You cannot use expr's double() function to perform the conversion, because expr fails with the same integer conversion error (as above) before calling double(). One simple way of casting an "integerstring" to a "doublestring" is to append a dot: set x $i. ;# or: append i "." This may however produce an ill-formed number if the numberstring contained a dot already. Hume Smith gave a clever solution in news:comp.lang.tcl : append i "e0" makes it look like scientific notation, meaning "multiplied by 10 to the 0th power, which is 1", which forces the string to a floatstring (i.e. that expr interprets as double) with pure string manipulation. If there is the slightest possibility that scientific notation occurs in the input, make it bullet-proof like if ![regexp "e" $i] {append i "e0"} ---- If you get an expression in from outside, and want it to be computed in float (in case it contains divisions), you can safeguard that with expr 1.0*$expression Here again, braces cannot be used, because the [expr] parser won't take operators in a variable... [RS] ---- Here's a solution from [Donal Fellows] that checks the error reason: # The absence of {curlies} from [expr] is crucial! if {[catch {expr double($int)} float]} { if {[string equal [lrange $::errorCode 0 1] "ARITH IOVERFLOW"]} { # We know we've got an int value now! set float [expr double($int.0)] } else { error "attempted conversion of non-numeric to float" } } ---- [Paul Welton] showed in [the comp.lang.tcl newsgroup] that you can get an '''unsigned string rep''' if you ask for it: format %u -1 => 4294967295 This can be sugared into a C-like declaration: proc unsigned var { uplevel trace var $var w "{set $var \[format %u \$$var\];#}" } ;#RS unsigned y set y -1 4294967295 But if you incr a variable which holds that value, you're still stuck at 0... ---- [Volker Hetzer] wrote in [the comp.lang.tcl newsgroup]: [expr -1 / 10] returns -1 !!! What's that? ''[Peter G. Baum] responded: '' That's integer division. From man expr: * / % ''Multiply, divide, remainder. None of these operands may be applied to string operands, and remainder may be applied only to integers. The remainder will always have the same sign as the divisor and an absolute value smaller than the divisor.'' So expr -1%10 must be >0 and < 10, and expr 10*(-1/10)+(1%10) must be -1. If you solve for "-1/10", you find, that -1 is the correct answer. [Steve Offutt] wrote: To prevent errors caused by integer division, either explicitly cast to double, or introduce a decimal point: % expr -1 / 10 -1 % expr double(-1) / 10 -0.1 % expr -1 / 10.0 -0.1 % expr -1.0 / 10 [Dan Kuchler] added: Of course, if you want 'integer' division, you have to do something like: expr {int(double(-1)/10)} ---- [Kevin Kenny] (23 May 2001) -- If you're dealing with integers that don't fit in 32 bits, but do fit in your double-precision floating point significand, you can sometimes use 'double' to get a little bit more precision. Consider: set x 0x0100 set y 0x1000FF1 set z [expr { double( $x ) * double( $y ) }] set upper [expr { int( $z / ( 1 << 24 ) )}] set lower [expr { int( $z - ( 1 << 24 ) * double( $upper ) ) }] puts [format {0x%06x%06x} $upper $lower] which prints: 0x0001000ff100 ---- [RS] Here's how you can find out your installation's MAXINT: expr 0x7[string range [format %X -1] 1 end] To test: set maxint [expr 0x7[string range [format %X -1] 1 end]] incr maxint should return a negative number with absolute value maxint+1. ---- Comparing doubles may also be [a real problem]. ''See also [Bag of algorithms] - [Bag of Tk algorithms] - [Mathematically oriented extensions]'' - [Category Mathematics]