Version 182 of expr

Updated 2014-07-17 18:43:13 by pooryorick

expr , a built-in Tcl command, evaluates an expression.

See Also

A little math language
adds features & sugar to expr
A real problem
Additional math functions
Arbitrary precision math procedures
Brace your expr-essions
compute
more sugar for expr
Converting numbers from arbitrary bases
DebuggingExprCalls
rwm sometimes it is difficult to debug expr calls where the operands are variables. DebuggingExprCalls explains how to wrap expr to help with these cases.
double substitution
exprlex and funclex
Split expressions and functions into their lexical components.
gotcha
where expr figures prominently
Tcl and octal numbers
The Octal Bug
expr problems with int
historical (pre-8.5) information on the limits of number representation (both integer and float) inherited from C
for
How can I do math in Tcl
if
Importing expr functions
use expr's functions without explicitly calling that, see Importing expr functions.
infix
a package to facilitate writing sequences of mathematical formulae in the familiar infix form
Math function help
Modeling COND with expr
Braced expressions can span several lines
Numerical Analysis in Tcl
Sample Math Programs
extremely simple math examples
Tcl help
TIP #123
Adding an Exponentiation Operator to expr
TIP #174
Math Operators as Commands
TIP #182
Add 'expr bool' Math Function
TIP #201
Add 'in' Operator to expr
TIP #201
Creating New Math Functions for expr (tcl::mathfunc)
TIP #237
Arbitrary-Precision Integers for Tcl
while

Documentation

officieal reference
man page
mathfunc
mathop man page
tcl_precision
Tcl_GetBoolean man page

Synopsis

expr arg ?arg arg ...?

Description

expr concatentates its arguments, evaluates this result as a Tcl expr implements a mini language that has a syntax separate from Tcl. It supports some of the same constructs as Tcl, such as variable substitution, command substitution, and braces. It adds additional syntax for mathematical operators and functions, and unlike Tcl, does not accept character strings that are not enclosed in double quotes or braces. expr implements a little language that has a syntax separate from Tcl. The expr conditions of commands such as if and while expect the expression to evaluate to a boolean, i.e., an integer or one of the following string values: A value recognized as boolean by string is boolean... can be used directly,

  • true, on, yes

The operators permitted in Tcl expressions include most of the operators

  • false, off, no

Functions take the form, Any of these values may be abbreviated, and upper-case spellings are also acceptable. See the Tcl_GetBoolean man page

name(argument?)

Boolean values such as true need not be enclosed in double quotes.

name(argument,argument ...)`

expr interprets "bare" strings as function names, e.g., cos($x), or as boolean values where context permits, and will raise an error if a bare word is encountered where a literal string is expected. Additionally, some operators (eq, ne, in, and ni) resemble bare strings.

`expr` [concat%|%concatentates] its arguments, evaluates this result as a [Tcl]
expression, and returns the value. The operators permitted in Tcl expressions
include most of the operators permitted in [C] expressions and a few additional
ones.  The operators have the same meaning and precedence as the corresponding
C operators. Expressions almost always yield numeric results (integer or
floating-point values). For example, the expression,

set val1 8.2
set val2 6
expr {$val1 + $val2}
expr $val1 + $val2

Result:
returns `14.2`.

Usually, though, it's best to brace the argument: In most cases, it's best to brace or otherwise expr {$val1+$val2}

This allows `expr` to do the interpretation, rather than having Tcl interpret
Any argument that can be interpreted in some way, e.g., variable or a command
expansion, should normally be enclosed in brackets.  This allows the
`expr` command to do the interpretation, rather than having the caller
interpret the arguments before handing them off to `expr`.  See below for
more details.
`expr` expressions differ from [C] expressions in the way that operands are
`expr`essions differ from C expressions in the way that operands are
(comparison) and lists (membership).



** Operators **

Since Tcl 8.5, many operators have command-equivalents in the
'''::[tcl::mathop]''' [namespace].

In spite of the name '''mathop''', some of the operators are string-oriented, rather
In spite of the name '''mathop''', some of the operators are string, rather

The following is a chart of operators, in order of precedence (tightest-binding
to least-tight associativity):
to least-tight binding):
&| [-] [+] [~] [!] | Unary operators; specifically a negation operation, a non-negation operation, a bit-wise NOT operation (every bit in the input value gets replaced by its inverse) and a logical NOT operation (non-zero maps to zero, and zero maps to one). |&
&| [**] | exponential. From Tcl 8.5 on. |&
&| [+] [-] | Addition and subtraction. |&
&| [<<] [>>] | Left and right shift.  Equivalent to multiplying or dividing by a suitable power of two, and then reducing the result to the range representable in an integer on the host platform. |&
&| [<] [>] [<=] [>=] | Ordering relations:  less than, greater than, less than or equal, greater than or equal.  These operations work on strings as well as numbers, but where string comparison is intended, it is advisable to use the dedicated string comparison operators or [string compare] or [string equal] instead,  as those are  more predictable in the case of a string that looks like a number.  For example, [string equal] considers "6" and "06" to be different strings, but the `expr`' operator `==` considers them to be equivalent numbers.|&
&| [==] [!=] | Equality and inequality.   These operations work on strings as well as numbers, but see the description equality operators for notes about string comparison. |&
&| [eq] [ne] | Since Tcl 8.4, these are string-comparison operators. "6" and "06", as well as 1 and 1.0, will compare unequal. |&
&| [in] [ni] | checks for occurrence of an item in a list. New in Tcl 8.5. |&
&| [**] | exponential. From Tcl 8.5 on. |&
&| [in] [ni] | checks for occurance of an item in a list. New in Tcl 8.5. |&
&| [^] | Bit-wise exclusive OR.  A bit is set in the result when the corresponding bit is set in ''precisely one'' of the arguments. |&
&| [<<pipe>>] | Bit-wise OR.   A bit is set in the result when the corresponding bit is set in either of the arguments. |&
&| [&&] | Logical AND.   The result is `1` when both of the arguments true. and `0` otherwise.  This operation is a ''short-circuiting'' operation, and will only evaluate its second argument when the first argument is non-zero.  This includes the expansion of Tcl commands in square brackets.  Where Tcl seems not to be behaving as describe here, see [double substitution]. |&
&| [<<pipe>><<pipe>>] | Logical OR.  The result is `0` when both of the arguments are false, and `1` otherwise.  This operation is a ''short-circuiting'' operation, and will only evaluate its second argument when the first argument is zero.  Where Tcl seems not to be behaving as describe here, see [double substitution]. |&
&| x'''?'''y''':'''z | If-then-else, as in [C]. `x`, `y`, and `z` are expressions.  The result is `y` if `x` is true, and `z` otherwise.  This operation is a ''short-circuiting'' operation:  If `x` is true, `z` will not be evaluated, and if `x` is false, `y` will not be evaluated.  Where Tcl seems not to be behaving as describe here, see [double substitution].  `[if]` performs just as well as this construct.  The generated bytecode is identical. |&



** Functions **

See the [http://www.tcl.tk/man/tcl/TclCmd/mathfunc.htm%|%mathfunc man page].

The following is a list of builtin functions:

   '''[abs]'''(x):   Absolute value (negate if negative).

   '''[acos]'''(x):   Inverse cosine (result in radians).

   '''[asin]'''(x):   Inverse sine (result in radians).

   '''[atan]'''(x):   Inverse tangent (result in radians).

   '''[atan2]'''(y,x):   Inverse tangent.  Can handle cases which plain `atan()` can't (due to division by zero) and has a larger output range (result in radians).

   '''[bool]'''(x):   Accepts any valid boolean value and returns the corresponding boolean value `0` or `1`.

   '''[ceil]'''(x):   Ceiling (defined over floating point numbers.)  If the input value is not a whole number, return the next ''larger'' whole number. '''Surprise''': The return value is a float, not an integer.

   '''[cos]'''(x):   Cosine (input in radians).

   '''[cosh function%|%cosh]'''(x):   Hyperbolic cosine.

   '''[double]'''(x):   Convert number to floating point.

   '''[entier]'''(x):   Like `int(x)`, but there is no limit to the input or output value.

   '''[exp]'''(x):   Exponential function.  Returns ''e'' to the power of `x`, where ''e'' is the base of natural logarithms.
   '''[exp]'''(x):   Exponential function.  Returns e to the power or `x`, where e is the base of natural logarithms.
   '''[floor]'''(x):   Floor (defined over floating-point numbers.)  If the input value is not a whole number, return the next ''smaller'' whole number as a floating-point number.

   '''[fmod]'''(x, y):   Floating-point remainder of x divided by y.

   '''[hypot]'''(x,y):   Hypotenuse calculator.  Assumes boring old Euclidean geometry.  If the projection of a straight line segment onto the X axis is `x` units long, and the projection of that line segment onto the Y axis is `y` units long, then the line segment is `hypot(x,y)` units long. Equivalent to `sqrt(x*x+y*y)`.

   '''[int]'''(x):   Convert number to integer by truncation.  Limited by the size of long in [C].

   '''[isqrt]'''(x):   Compute the integer part of the square root of `x`.

   '''[log]'''(x):   Natural logarithm.

   '''[log10]'''(x):   Logarithm with respect to base 10.

   '''[max]'''(x,...):   The argument with the greatest value.
   '''[max]'''(x,...):   Return the one argument with the greatest value.
   '''[min]'''(x,...):   The argument with the least value.
   '''[min]'''(x,...):   Return the one argument with the least value.
   '''[pow]'''(x,y):   Power function.  `x` to the power of `y`.

   '''[rand]'''():   Random number.  Uses uniform distribution over the range [0,1).  ''This RNG is not suitable for cryptography.''

   '''[round]'''(x):   Round to nearest whole number.  ''Not suitable for financial rounding.''

   '''[sin]'''(x):   Sine (input in radians).

   '''[sinh]'''(x):   Hyperbolic sine.

   '''[sqrt]'''(x):   Square root (well, the positive square root only.  And Tcl doesn't do complex math, so the input had better be positive).

   '''[srand]'''(x):   Seeds the random number generator with the given value.  Each interpreter has its own random number generator, which starts out seeded with the current time.

   '''[tan]'''(x):   Tangent (input in radians.)

   '''[tanh]'''(x):   Hyperbolic tangent.kjh

   '''[wide]'''(x):   Take any numeric value, and return the low order 64 bits of the integer value of the argument



** Mathematical Expressions **


Simple addition:

set a expr {1 + 2}

mathematical functions

set a expr {sqrt(4)}

[martin Lemburg]: The following returns `1` because `" 2 "` will be interpreted
as `2`:
as to `2`:

set a expr {" 2 " == [string trim " 2 "}]

To ensure that expression evaluates to a floating point number, use `double()`
or `floor()`:

set a 1 set b 2 expr {double($a)/$b}

or, to get an integer:

expr {entier($a/$b)}

`int()` would also have worked, but `entier()` is more general


** Order of Precedence **

The following returns returns 4, rather than -4 as some might expect:

set a expr {-2**2}

The following returns `1` because `2==2` is evaluated first:
returns `1` because `2==2` is evaluated first

set a expr {5&2==2}

[AMG]: The order of bitwise operations (`|`, `&`, and `^`) may seem totally bogus, but it's inherited from [C], which in turn inherited it from an early prototype version of C which lacked separate logical operators (`&&` and `||`) [http://cm.bell-labs.com/who/dmr/chist.html]. I wouldn't cry if a new language (not Tcl) decided to break compatibility with C in this respect.
** Composing Expressions **

`expr` tries to interpret operands as numeric values, but doesn't parse 
`expr` tries to interpret operands as numeric values, but it does not try
to interpret variable values as complete numeric expressons, so a value "2*3"
will be interpreted as a string:

set y 2*3; expr {$y} ;# ==> 2*3 set y 2*3; expr {$y+2} ;# ==> can't use non-numeric string as operand of "+"

To pass a complete expression stored in a variable, omit the braces so that Tcl
To pass a complete expression stored in a variable, take advantage of [double substitution] by omitting braces:

set y 2*3; expr $y ;# ==> 6 set y 2*3; puts expr $y+2 ;# ==> 8

But be careful not to introduce an [injection attack] vulnerability.  See
[double substitution].



** Literal String Operands **

`expr` implements a little language distinct from the language described in [dodekalogue%|%the rules of Tcl].  One
`expr` implements a little language distinct from standard Tcl.  One

% if {joe eq mike} {puts wow} syntax error in expression "joe eq mike": variable references require preceding $ % if {"joe" eq "mike"} {puts wow} % if {{joe} eq {mike}} {puts wow}

To insert a literal value when templating an expression, use an [identity
If you prefer consistency, then throughout your Tcl program, always quote values you consider to be "strings". ([AMG]: This is a totally arbitrary and stylistic distinction because [everything is a string].) Note that at some point `expr` may grow the feature of accepting bareword strings. ([AMG]: Or it might choose to interpret barewords as variable substitutions.)

If the return value of `expr` is numeric, it is transformed into a canonical
If the value of an `expr` is numeric, it will transformed into a canonical

set val 0x10 puts $val ;# 0x10 set val expr {$val} puts $val ;# 16

puts expr {[join {0 x 1 0} {}}] ;# 16


In other words, `expr` may mutate strings that can be interpreted as
numbers, which is a potential [gotcha] when using string functionality
numbers, which is a potential gotcha for the programmer using string functions
interpretation.



** The Octal Bug **
** [Tcl and Octal Numbers%|%The Octal Bug] **
See [Tcl and Octal Numbers] for details.

[rjm]: Why does `expr` return integers with leading zeroes as hex?, e.g.

expr 0100 ;# -> 64

[AMG]: The leading zero makes 0100 octal.  The 1 is in the 64's place, hence
the result is 64 in decimal.  That's not hexadecimal.  `expr` always
returns a decimal value; you have to use `[format]` if you want it in some
other base.

[RJM]: I came around this as I was going to do calculus on a four-digit
formatted number in an entry field. I had to apply `scan $var %d` to get rid
formatted number in an entry field. I had to apply ''scan $var %d'' to get rid
language...


`expr` uses floating point arithmetic, so strings representing decimal
fractions that don't have a precise floating-point representation will be given
to a close-enough representation.  In the following example, `36.37` gets a
to a close-enough representation.  In the following example, "36.37" gets a

expr {int(36.37*100)}

If that value is subsequently used as a string, it becomes necessary to somehow
convert it.  Over the years, the default string conversion has varied.  For Tcl
version 8.5.13, it looks like

3636.9999999999995

[RS] points out that version 8.4.9 provided the following results, and that
that braced or not, `expr` returns the same (string rep of) double as well
as integer, so the issue of bracing one's expressions is not relevant to the
issue of floating-point to string conversion.

% expr 36.37*100 3637.0 ;#-- good enough... % expr {36.37*100} 3637.0 ;#-- the same % expr {int(36.37*100)} 3636 ;#-- Hmm % expr int(36.37*100) 3636 ;#-- the same % info pa 8.4.9

One way to get `3637` would be to use `round()`:
One way to get "3637" would be to use `round()`:

expr {round(36.37*100)}

`[format]` can also be useful, but the main point is to remain
aware of the context and decide if and how to use floating-point operations.

[LV]:  My response on [comp.lang.tcl] was that I thought it was a shame
that `expr` (or perhaps it is Tcl) didn't use the same mechanism for
both calculations of 36.37 * 100 ; that way, the results would at least
be consistent.  Even if they were consistently '''wrong''', one would
be able to at least to live within the ''law of least surprise''.
As it is, until one experiments, one won't know which way that Tcl
is going to ''round'' results.

[EPSJ]:  This may be a side effect of the IEEE floating point standard. This is
done in hardware to guarantee the convergence in the case of a series of math
algorithms. The rule is that the mantissa of a floating point number must be
rounded to the nearest even number. As 36.37 cannot be represented exactly in
float point it ends up being a small fraction below the intended number. On the
other side 36.38 moves on the other direction. Look the following result:

() 60 % expr int(36.380*100) 3638 () 61 % expr int(36.370*100) 3636

x86 floating point hardware allows this to be configurable to nearest even,
nearest odd, and a few more options. But usually nearest even is the default.
The result may seem inconsistent, but it is intentional.


** pow() vs **
** pow() vs ** **
[LES] 2005-07-23:

% expr pow(5,6) 15625.0

% expr 5**6 15625

Two syntaxes, two slightly different results. Is that intentional?

[RS]: Yes.  While `pow()` always goes for double logarithms, `**` tries to do
integer exponentiation where possible.


** Precision **

[davou]: What is the precision of functions in `expr`, and how can it be
expanded upon?

[Lars H]: That's generally determined by the [C] library functions that
implement them. It depends on where (and against what) Tcl is compiled.  For
real numbers, that means '''double'''s, which are floating-point numbers of
typically about 17 decimal digits precision (but how many of these are correct
varies between functions and platforms). For integers Tcl has traditionally
used '''long'''s, which in most cases means 32-bit two's complement integers
(`$tcl_platform(wordSize)` tells you the actual number of bytes), but as of Tcl
8.5, it supports (almost) arbitrarily large integers ([googol magnitude] is no
problem anymore, whereas googolplex magnitude wouldn't fit in the computer
memory anyway). As for extending what the core provides, [tcllib] provides
[math::bignum] and [math::bigfloat].


** [Nan] and [Inf] **


At least as of Tcl 8.5, [NaN] and [Inf] are potential values returning from
`expr`.

[Philip Smolen] I've never seen expr return NaN.  I wish it would!

Interactions with locale

Interactions with locale

parsing of decimals in expr may be hampered by locale - you might get

expr concatenates its arguments into an expression. Consider the following expr resolves variables in the context of its caller, and each variable value becomes exactly one operand in the expression. Consider the following:

expr 5 > {} ;# -> missing operand at _@_
set color2 green

expr {$color1} eq {$color2} ;# 1

Concatenated, the arguments form the script, `green eq green`, in which the two
But if the arguments are not bracketed, it is a syntax error:
Another example illustrating the same point:

#wrong expr $color1 eq $color2

invalid bareword "green" in expression "green eq green"; should be "$green" or "{green}" or "green(...)" or ...

This is because in `expr` syntax, strings should be quoted or bracketed

set a abc set a "abc" expr $a in $b # invalid bareword "abc" # in expression "abc in 123 abcd xyz lmnop"; # should be "$abc" or "{abc}" or "abc(...)" or ...

expr {$a in $b} ;#-> 0 expr {$a in $b} # 0 expr {$a ni $b} ;#-> 1 expr {$a ni $b} # 1

% expr $a eq "foo" ? true : false
% expr $a == "foo" ? true : false
% expr {$a eq "foo" ? true : false}
% expr {$a == "foo" ? true : false}

When exactly one unconcatenated value is passed to expr, the argument can be expr is much more performant when it has exactly one argument. The argument must either be brace-quoted or be a single variable substitution, since this allows byte-compilation.

AMG: More precisely, the argument must not contain multiple substitutions or be the concatenation of substitutions and literal text. The goal is for there If expr has to concatenate its arguments (i.e. it is passed more than one argument), or if Tcl has to concatenate the results of multiple substitutions and literal substrings, then the math expression will be in a temporary Tcl_Obj which must be regenerated every time expr is called.

Fast:

expr {2 + 2}         ; # Preferred
expr 2+2             ; # Valid but lazy (1)
expr "2 + 2"         ; # Valid but not preferred (2)
expr 2\ +\ 2         ; # Valid but ugly (3)
expr $expression     ; # Valid
expr [expression]    ; # Valid

(1) This style is easy to type and is fine for interactive use, but you will lose performance (and correctness and security) if you use this notation in combination with variable and script substitutions.

(2) Same problems as (1). Use braces instead.

(3) Same problems as (1), plus you might forget a backslash before a space, thereby forcing expr to concatenate its arguments.

Slow:

expr 2 + 2           ; # Slow since [expr] must concatenate its arguments
expr 2 + $x          ; # Slow since [expr] must concatenate its arguments, also unsafe
expr 2+$x            ; # Slow since Tcl must concatenate to determine argument, also unsafe
expr "2 + $x"        ; # Slow since Tcl must concatenate to determine argument, also unsafe

AMG: The security problems of unbraced expressions are very similar to SQL injection attacks. Notice how sqlite's Tcl binding does its own SQL injection attacks. Notice how sqlite's Tcl binding does its own this problem as well because the default is to apply multiple passes of interpretation.

See also double substitution. See also double substitution

AMG: The above speed and security concerns also apply to if, for, and while since they share the expression engine with expr.

Additionally, for and while really do need their expressions to be braced. In order for the loop to execute a nonzero but finite number of times, the expression's value must not be constant; but if they're not braced, their value is determined before the loop can begin.

The exception is when the expression (as opposed to value) is contained in a variable, in which case it must not be brace-quoted, or else the command would try to treat the expression as a (string) value and almost certainly fail to convert it to a boolean value.

DKF: But even then, for if, for and while you must still brace the expression to avoid being stuck in the compilation slow lane. Putting an expr inside can help:

set ex {$f > 42}
while {[expr $ex]} {
    puts "This is the body: f is now [incr f -1]"
}

Consider what would happen if this script were actually working with user input:

#DON'T EXECUTE THIS SCRIPT!!!
set x {[exec format C:\\]}
set j {[puts Sucker!]}
#C:\ get formatted in the next command
set k [expr $x / $j.]

On the other hand,

set k [expr { $x / double($j) }]

gives a much more reasonable result:

argument to math function didn't have numeric value
   while executing
"expr { $x / double($y) }"
   invoked from within
"set k [expr { $x / double($y) }]
"
    (file "foo.tcl" line 3)

Unless you know exactly what you are doing, unbraced expressions are not recommended. Nevertheles...

With unbraced expressions, . (\x2e) can be appended to a variable to get expr With unbraced expressions, a "." can be appended to a variable to get expr

set x 1; set j 2

# works (but don't do this)
expr $x/$j.

#an accepted way to do it
expr {double($x)/$j}

# error: syntax error in expression "$x/$j."  (expr parser)
expr {$x/$j.}

It's faster, too:

set script1 {
   set x 1
   set j 2
   set k [expr $x / $j.]
}
set script2 {
   set x 1
   set j 2
   set k [expr { $x / double($j) }]
}
foreach v {script1 script2} {
foreach v { script1 script2 } {
}

#script1: 38 microseconds per iteration
#script2: 9 microseconds per iteration

#[pyk] 2012-11-28: what a difference a few years makes (an "old" 3.06Ghz Intel Core 2 Duo):
#script1: 4.4767364 microseconds per iteration
#script2: 0.7374299 microseconds per iteration

RS: This was just to demonstrate the differences between the regular Tcl parser and the parser for expr', not recommended practice. Another example is substitution of operators:

set op +
expr 4 $op 5
9
expr {4 $op 5}
syntax error in expression "4 $op 5"

See the for page on a case where that helped.

Bytecode compilation and performance

AMG: When expr's argument is properly braced, the expression can be bytecoded for significant performance gains. However, the performance is not always quite as good as one would hope. Use tcl::unsupported::disassemble to see this in action:

% tcl::unsupported::disassemble script {expr {4 / 3. * acos(-1) * $r ** 3}}
ByteCode 0x00000000025FADB0, refCt 1, epoch 16, interp 0x00000000026365B0 (epoch 16)
  Source "expr {4 / 3. * acos(-1) * $r ** 3}"
  Cmds 1, src 34, inst 17, litObjs 5, aux 0, stkDepth 3, code/src 0.00
  Commands 1:
      1: pc 0-15, src 0-33
  Command 1: "expr {4 / 3. * acos(-1) * $r ** 3}"
    (0) push1 0         # "1.3333333333333333"
    (2) push1 1         # "tcl::mathfunc::acos"
    (4) push1 2         # "-1"
    (6) invokeStk1 2 
    (8) mult 
    (9) push1 3         # "r"
    (11) loadStk 
    (12) push1 4         # "3"
    (14) expon 
    (15) mult 
    (16) done 

This shows that 4 / 3. is precomputed to 1.3333333333333333, but acos(-1) is not precomputed to 3.141592653589793. While it would seem ideal to fold the constants together into 4.1887902047863905, doing so would skip invoking tcl::mathfunc::acos, which might have a trace on it. Tcl optimizations always favor correctness over speed, so this shortcut is not available. This shows that "4 / 3." is precomputed to "1.3333333333333333", but "acos(-1)" is not precomputed to "3.141592653589793". While it would seem ideal to fold the constants together into "4.1887902047863905", doing so would skip invoking tcl::mathfunc::acos, which might have a trace on it. Tcl optimizations always favor correctness over speed, so this shortcut is not available. Here's the above again, but with local variables which provide a large speed boost by avoiding looking up the variable by name:

% tcl::unsupported::disassemble lambda {{} {expr {4 / 3. * acos(-1) * $r ** 3}}}
ByteCode 0x00000000027A1080, refCt 1, epoch 16, interp 0x00000000026E65B0 (epoch 16)
  Source "expr {4 / 3. * acos(-1) * $r ** 3}"
  Cmds 1, src 34, inst 16, litObjs 4, aux 0, stkDepth 3, code/src 0.00
  Proc 0x00000000026A8620, refCt 1, args 0, compiled locals 1
      slot 0, scalar, "r"
  Commands 1:
      1: pc 0-14, src 0-33
  Command 1: "expr {4 / 3. * acos(-1) * $r ** 3}"
    (0) push1 0         # "1.3333333333333333"
    (2) push1 1         # "tcl::mathfunc::acos"
    (4) push1 2         # "-1"
    (6) invokeStk1 2 
    (8) mult 
    (9) loadScalar1 %v0         # var "r"
    (11) push1 3         # "3"
    (13) expon 
    (14) mult 
    (15) done 

(For disassembly readouts, it's not necessary to list the variables as arguments to the lambda. They'll be assigned slots in the compiled locals table either way. You're not actually running the code, so it doesn't matter if the variable exists.)

Common subexpressions cannot be optimized because this would bypass some potential traces on variable access and procedure invocation. If expr could know in advance that particular procedures and variables don't have traces, it would have greater freedom to perform common subexpression elimination. Knowing that a procedure is a pure function (its result depends only on its arguments), plus knowing that its definition will not change throughout the execution of the program, would let expr treat acos(-1) as a constant. Common subexpressions cannot be optimized because this would bypass some potential traces on variable access and procedure invocation. If expr could know in advance that particular procedures and variables don't have traces, it would have greater freedom to perform common subexpression elimination. Knowing that a procedure is a pure function (its result depends only on its arguments), plus knowing that its definition will not change throughout the execution of the program, would let expr treat acos(-1) as a constant. Now rearrange the expression to put the division at the end. Algebraically, this should produce an identical result. But because of potential floating point precision issues (non-commutativity of operations), Tcl must play it safe and do the operations in the order specified:

% tcl::unsupported::disassemble script {expr {4 * acos(-1) * $r ** 3 / 3.}}
ByteCode 0x00000000025F91B0, refCt 1, epoch 16, interp 0x00000000026365B0 (epoch 16)
  Source "expr {4 * acos(-1) * $r ** 3 / 3.}"
  Cmds 1, src 34, inst 20, litObjs 6, aux 0, stkDepth 3, code/src 0.00
  Commands 1:
      1: pc 0-18, src 0-33
  Command 1: "expr {4 * acos(-1) * $r ** 3 / 3.}"
    (0) push1 0         # "4"
    (2) push1 1         # "tcl::mathfunc::acos"
    (4) push1 2         # "-1"
    (6) invokeStk1 2 
    (8) mult 
    (9) push1 3         # "r"
    (11) loadStk 
    (12) push1 4         # "3"
    (14) expon 
    (15) mult 
    (16) push1 5         # "3."
    (18) div 
    (19) done 

Back to common subexpression elimination: It may seem that the solution is for the programmer to manually precompute common subexpressions and reference their values via variables. This generally helps, so long as the subexpressions aren't too simple, but you must use local variables or else performance will suffer:

% proc a {x} {expr {cos($x * acos(-1)) + sin($x * acos(-1))}}
% proc b {x} {set y [expr {$x * acos(-1)}]; expr {cos($y) + sin($y)}}
% proc c {x} {set ::y [expr {$x * acos(-1)}]; expr {cos($::y) + sin($::y)}}
% set x 12.3
% time {a $x} 1000000
1.536581 microseconds per iteration
% time {b $x} 1000000
1.333106 microseconds per iteration
% time {c $x} 1000000
1.994305 microseconds per iteration

In Embedded vs. separate commands , 1992-12-38, JO published the voting results 37:8 in favor of embedded functions() vs. separate [commands]

(Tcl 8.4 and older) 32-bit integer limitations

Addition

% expr (1<<31)-1
2147483647

% expr 2147483647 + 2147483647
-2

Multiplication

% expr sqrt((1<<31)-1)
46340.9500011

expr 46341*46341
-2147479015

These are results of Tcl 8.4 and older versions using a 32-bit representation for integers.

Tcl 8.5 features abritrary-precision integers. See TIP #237 .

Suggestion to reparse expr variables

expressions in variable values would interpreted as such

RS 2003-04-24: Here's a tiny wrapper for friends of infix assignment:

proc let {var = args} {
    uplevel 1 set $var \[expr $args\]
} ;#RS
% let i = 1
1
% let j = $i + 1
2
% let k = {$i + $j}
3
set y 2*3; puts [expr $y+0] ;# ==> 6

AM: The problem with variables whose values are actually expressions is that they change the whole expression in which they are used. The performance gain for caching the parsed expression will then be lost.

AMG: This reopens the door to all the security, performance, and correctness problems solved by bracing one's expressions.

Unsuitability of expr for time offset calculations

Wookie: I had some trouble recently using expr to calculate time offsets. I had 2 time stamps in the form hh:mm

So I had 4 variables h1, m1, h2, m2 and one of my expr functions was

set result [expr {$m1 + $m2}]

As many of you may be thinking, you fool! what about 08 and 09, which will get treated as invalid octal. So after some grumbling I thought okay so I have to trimleft them. Bit verbose but who cares:

set m1 [string trimleft $m1 0]
set m2 [string trimleft $m2 0]
set result [expr ($m1 + $m2)]

Now what could possibly go wrong with that... well obviously 00 becomes the empty string, which causes unexpected closed parameter in the expression. So now I have to check for the empty string. So...

set m1 [string trimleft $m1 0]
if {$m1=={}} {set m1 0}

set m2 [string trimleft $m2 0]
if {$m2=={}} {set m2 0}

set result [expr {$m1 + $m2}]

... and then repeat it for the hours. It all seemed very clumsy. So I came up with this, which may solve many of the conversion issues in this section.

scan "$h1:$m1 $h2:$m2" "%d:%d %d:%d" h1 m1 h2 m2
set result [expr {$m1 + $m2}]

All the conversions to int have been done and leading 0's have been stripped and returns 0 if the value is all 0s. This works for float and probably double (though I've not tried). Can anyone see any problems with this approach?

glennj: No, scan is definitely the way to parse numbers out of dates and times. However, for date arithmetic, nothing beats clock.

# adding a delta to a time
set h1 12; set m1 45
set h2 3; set m2 30
clock format [clock add [clock scan "$h1:$m1" -format "%H:%M"] $h2 hours $m2 minutes] -format %T ;# ==> 16:15:00

What are you trying to do with your two times?