Main Contents »

Copyright © 2015 Arjen Markus. All rights reserved.

1. Introduction

In almost all programs you need to do some calculations with numbers or check for a particular condition. Tcl’s approach to both is via the expr command, though you do not need to use it explicitly in the context of for, while and if. The syntax is quite what you are used to from high-school mathematics, except that you use a "$" in front of variables. Here is a very simple example:

for { set n 0 } { $n < 10 } { incr n } {
    puts "The square root of $n is: [expr {sqrt($n)}]"
}

which produces the square roots of the integers 0 to 9. To access the value of the variable n we use $n, just as in any ordinary Tcl code. This is also true for accessing the value of array elements:

set i 0
expr {$a(b) + $number($i)}

adds the value of array element $i (=0) in the array "number" to the value of array element "b" in the array "a".

While Tcl’s basic syntax uses prefix notation, that is, the first word identifies the command, the syntax of expr’s expressions uses 'infix' notation. You might say that expr defines its own sublanguage. In this it is not unique: regular expressions for example form their own sublanguage too, both dedicated to the problem these commands are designed for.

If you prefer the prefix notation, you can use all operations and functions as if they were ordinary Tcl commands too. See the section Prefix notation.

Note This chapter assumes that you use Tcl 8.5 or later. Some of the features discussed here do not exist in Tcl 8.4 or earlier.

2. Conditions - implicit use of the expr command

The commands if, for and while use the expr command implicitly:

if { $x > 0 } {
    puts "x is positive"
}

It is therefore not necessary to do something like:

if { [expr {$x*$x + $y*$y}] > 4.0 } {
    puts "Point outside the circle"
}

It is much better (more concise, clearer) to use:

if { $x*$x + $y*$y > 4.0 } {
    puts "Point outside the circle"
}

or even (taking advantage of the built-in function hypot() to calculate the length of a vector):

if { hypot($x,$y) > 2.0 } {
    puts "Point outside the circle"
}

3. Two simple examples

Here we illustrate the use of expr and several commands that use it implicitly by calculating the solutions to a number-theoretical problem and a fairly basic physical system.

3.1. Pell’s equation

The code below can be used to find solutions to the so-called Pell’s equation, an equation of interest in number theory. The equation itself is deceptively simple (at least that is one particular form):

x2 - D y2 = 1

where D is an integer and the problem is to find integer solutions (x,y).

Let us set D to 5 and search for solutions with x and y between 0 and 10000. A straightforward implementation is:

set D 5
for {set y 0} {$y <= 10000} {incr y} {
    for {set x 0} {$x <= 10000} {incr x} {
        if { $x**2 - $D *$y**2 == 1 } {
            puts "$x - $y"
        }
    }
}

This works fine and produces the following list:

1 - 0
9 - 4
161 - 72
2889 - 1292

But if you look more closely at the two loops, you will note that we are spending a lot of iterations in areas where no solution can exist: if x and y get bigger than, say, 10, their ratio will converge to sqrt(5), or 2.236…​. Hence, why not look for solutions where x is about 2.236*y?

That is what the fragment below does:

set D 5
for {set y 0} {$y <= 10000} {incr y} {
    set xmin [expr {int( sqrt($D)*$y ) - 3}]  ;# Convert to integers!
    set xmax [expr {int( sqrt($D)*$y ) + 3}]
    #
    set x $xmin
    while { $x <= $xmax } {
        if { $x**2 - $D *$y**2 == 1 } {
            puts "$x - $y"
        }
        incr x
    }
}

The result is slightly different - it picks up one of the solutions with negative x, but the program is much faster:

-1 - 0
1 - 0
9 - 4
161 - 72
2889 - 1292
Note Try this with different values of D. You may be surprised to see that for D = 61 no solution is printed. That is because the smallest solution is (x,y) = (1766319049,226153980). But you can check that it is indeed a solution.

3.2. A damped harmonic oscillator

A completely different problem is that of the damped oscillator, such as a pendulum (well, in first approximation). The differential equation is:

m y” + r y’ + k y = 0

with m, r and k given parameters and y the excitation at some moment in time. You need two initial conditions: the excitation at time T0 and the velocity at that moment, so solve the equation.

The equation shown here is simple enough to solve analytically, but in general you need to rely on numerical methods. The simplest such method is that by Euler. First the equation is written as a system of two first-order equations:

y’ = v

m v’ + r v + k y = 0

where v is the velocity of the oscillator.

Then the solution at time T+dt is approximated as (dt is the time step):

ynew = yold + dt * y’old

vnew = vold + dt * v’old

where the subscripts 'old' and 'new' refer to the previous and the current times.

Looping over the steps we get the approximate solution to the original equation:

set excitation 0.0
set velocity   1.0
set damping    0.1
set fcoeff     1.0
set dt         0.05
for {set t 0} {$t < 100} {incr t} {
    #
    # Calculate the derivatives
    #
    set d_excitation $velocity
    set d_velocity   \
        [expr {-$damping * $velocity - $fcoeff * $excitation}]
    #
    # Calculate the new values
    #
    set excitation  [expr {$excitation + $dt * $d_excitation}]
    set velocity    [expr {$velocity   + $dt * $d_velocity}]
    #
    puts "[expr {$t*$dt}] $excitation $velocity"
}
Note The math::calculus package in the Tcllib library implements several methods to solve systems of first-order differential equations.

4. Numbers and strings

The expr command deals with both numbers and strings. The code:

set string "Book"
puts [expr {$string == "Book"}]

prints the value 1, indicating that the condition is satisfied, that is the value of the variable string is equal to the literal string Book.

This example reveals a difference between literal values in Tcl and in the expr expression sublanguage. In Tcl the following two commands are exactly the same:

puts Hello

and

puts "Hello"

In expr the literal string "Hello" without quotes leads to a complaint:

expr Hello

produces:

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

(Just try it in an interactive session)

Another aspect that you need to be aware of is that expr prefers numbers over strings: it will first try to convert a string value to a number and if that does not succeed, it will use the string as such. Therefore:

set a "1.0"
set b "+1"
expr {$a == $b}

return 1, indicating that the 'numbers' $a and $b are the same.

If you want to compare strings, use the eq ("the strings are equal") and ne ("the strings are not equal") operations. This has the further advantage that expr will not try to convert the strings to numbers.

Note There are no equivalents for the "greater than" and similar operations that work exclusively on strings. If you want to ensure strings are treated as strings in a logical expression, you need to use the string compare command.

The expr command also provides shorthand operations for searching elements in a list:

set list {A B C D}
set element1 "D"
set element2 "Z"
expr {$element1 in $list}   ;# Is the element _in_ the list?
expr {$element2 ni $list}   ;# Is the element _not in_ the list?

will print:

1
1

as the element "D" is found in the list (the first 1, so the expression results in "true") but "Z" is not (the second 1, so this expression is also true). The two operations use the exact matching method as found in the following command:

lsearch -exact $list $element1

though this command returns the position of the element or -1, if the element was not found. Therefore the in operation is equivalent to:

expr {[lsearch -exact $list $element1] >= 0}

5. Integers and floating-point numbers

Like most computer languages, Tcl distinguishes 'integer' numbers and 'floating-point' numbers. While they can be mixed in calculations and are (usually) converted from one type to the other without you having to worry about it, you do need to be aware of a few caveats.

'Dividing two integers'

If you calculate something like "100 divided by 2", you get 50, just as expected. If you calculate "2 divided by 100", however, the answer is 0, not 0.02! The reason is that Tcl, like most computer languages, uses 'integer division' if the two operands are integer.

To force the outcome to be a fraction, use a function like double():

expr {2 / double(100)}

results in:

0.02

You can of course also turn one of the constants into a floating-point value:

expr {2.0 / 100}

'Different types of integers'

Tcl has three types of integers, 32-bits integers, 64-bits integers and arbitrary-range integers. Normally they are converted automatically into the appropriate type, but several functions are available to force a value into one of these three: int(), wide() and entier(). See the section on standard functions.

Each type has its own storage requirements and its own limitations as to the numbers that can be stored.

'Floating-point numbers'

A frequently asked question concerns the very nature of floating-point numbers. Many people are awed by the following:

set factor 2.01
puts [expr {int(100*$factor)}]

producing

200

not the answer they expect: 201.

Actually, puts [expr {100*$factor}] gives:

200.99999999999997

The reason for this is that the number 2.01 can not be represented exactly by the underlying 'binary' floating-point system, just as 1/3 can not be exactly represented as a decimal number.

This can be remedied in three ways:

  • Use the round() function, so that the nearest integer is used, instead of the one obtained by truncating the floating-point number

  • Use a 'decimal arithmetic' package, such as the one included in the Tcllib library.

  • Accept this fact of life.

The latter is the easiest approach - contemporary computers simply use binary floating-point arithmetic throughout!

6. Using functions

The expr command has a full complement of the common mathematical functions, such cos, sin, log and log10:

expr {acos(-1.0)}

produces:

3.141592653589793

the nearest approximation of the mathematical constant pi that can be obtained with double-precision floating-point numbers.

Note If you need numbers like 'ln(2)' or the conversion factor of degrees to radians often, you may find the math::constants package in Tcllib useful. It defines a small set of such constants that can then be selectively imported as namespace variables.

To tabularise a function like 'sin(x) * exp(-x)' for x from 0 to 5 you can use the following code:

puts "x\tf(x)"
for { set i 0 } { $i < 100 } { incr i } {
    set x [expr {$i * 0.05}]
    puts "$x\t[expr {sin($x) * exp(-$x)}]"
}

If you need to use the result of some command, say you want to know the position of an element in the list, rather than if it is in the list, this is done via […​] just as in Tcl itself:

set list {A B C D}
set element1 "B"
expr {1 + [lsearch $list $element1]}

which results in the index of the 'next' element:

2
Note The default matching method for lsearch is -glob, which means that an asterisk (*) or a question mark (?) in the string to be found in the list may lead to a different element than you expect.

6.1. User-defined functions

Sometimes it is useful to define your own functions for use in expr. For instance, if you use angles expressed in degrees and need to use their sines and cosines, converting the angles to radians and then using the result as arguments to cos or sin can become tedious. By defining procedures in the namespace tcl::mathfunc these procedures become available as functions in expr:

namespace eval tcl::mathfunc {
    variable degtorad
    set degtorad [expr {acos(-1.0)/180.0}]
}
proc tcl::mathfunc::sind {x} {
    variable degtorad
    expr {sin($degtorad*$x)}
}
proc tcl::mathfunc::cosd {x} {
    variable degtorad
    expr {cos($degtorad*$x)}
}
foreach degree {0 30 45 60 90} {
    puts "$degree [expr {sind($degree)}] [expr {cosd($degree)}]"
}

which results in:

0 0.0 1.0
30 0.49999999999999994 0.8660254037844387
45 0.7071067811865475 0.7071067811865476
60 0.8660254037844386 0.5000000000000001
90 1.0 6.123233995736766e-17
Note
  • The namespace tcl::mathfunc is 'relative' to the current namespace. This means that you can specify new mathematical functions or redefine existing ones in the current namespace without influencing other namespaces. This is especially important when designing packages.

  • While you can define procedures in the relative namespace tcl::mathop too, these are not made available as new operations, unfortunately. The current implementation of the expr command, especially its parser, does not make this possible.

7. Overview of the operations

The operations defined by the expr command follow the usual precedences: multiplication (*) takes precedence over addition (+) for instance. You can use parentheses to force evaluation to take place in a different order:

expr {1 + 2 * 3}

is evaluated as: multiply 2 by 3 and add the result to 1. Result: 7.

expr {(1 + 2) * 3}

is evaluated as: add 1 and 2 and multiply the result by 3. Result: 9.

Most operations are left-associative:

expr {2 - 2 - 3}

is evaluated as:

expr {(2-2) - 3}

So the result is -3.

The only exception is the exponentiation operation, just as ordinary convention would have it:

expr {2**3**5}

is evaluated as:

expr {2**(3**5)}

not as

expr {(2**3)**5}

So the result of expr {2**3**5} is 14134776518227074636666380005943348126619871175004951664972849610340958208, yes a pretty large number. (The result of expr {(2**3)**5} is: 32768 or 2**15).

Table 1. Arithmetic operations

x + y

Add x and y

x - y

Subtract y from x

+ x

Unary plus (no effect)

- x

Unary minus (multiply x by -1)

x * y

Multiply x and y

x / y

Divide x by y (integer division if both x and y are integers!)

x ** y

Take x to the power y

x % y

Take the modulo x wrt y (integers only)

Table 2. Comparison operations

x == y

True (1) if x is equal (numerically or by string comparison) to y; false (0) otherwise

x != y

True (1) if x is not equal (numerically or by string comparison) to y; false (0) otherwise

x > y

True (1) if x is greater (numerically or by string comparison) than y; false (0) otherwise

x >= y

True (1) if x is greater (numerically or by string comparison) than y or equal to y; false (0) otherwise

x < y

True (1) if x is lower (numerically or by string comparison) than y; false (0) otherwise

x ⇐ y

True (1) if x is lower (numerically or by string comparison) than y or equal to y; false (0) otherwise

x eq y

True (1) if x is equal to y by string comparison. If either is a number, it is converted to a string first

x ne y

True (1) if x is not equal to y by string comparison. If either is a number, it is converted to a string first

Note The comparison operations will first try to compare the operands numerically and if that fails, the operands are treated as strings.
Table 3. Logical operations

!x

Negation of x (if x is true, then !x is false; if x is false, then !x is true)

x || y

True (1) if x is true or y is true; false (0) otherwise

x && y

True (1) if x is true and y is true; false (0) otherwise

x ? y : z

If the expression x is true, evaluates to y, otherwise to z

x in y

True (1) if the string x is contained as an element in the list y

x ni y

True (1) if the string x is 'not' contained as an element in the list y

Note
  • Logical values in Tcl are 0 (false) and 1 or better: anything not zero (true). The logical operations return either 1 or 0, but the arguments can be any value - these are simply interpreted as true or false according to the above scheme.

  • Strings like "true" and "yes", "false" and "no" are also treated as valid logical values:

    set conclusion no
    if { $conclusion } {
        puts "The conclusion is true!"
    }
Bitwise operations

The bitwise operations can be used on integer values only.

~x

Negation of each bit in x

x & y

Bitwise AND on each pair of bits in x and y

x | y

Bitwise OR on each pair of bits in x and y

x ^ y

Bitwise exclusive-OR on each pair of bits in x and y

x << y

Shift all bits in x to the left by y positions

x >> y

Shift all bits in x to the right by y positions

Note Right bit shifts operations are tricky when it comes to negative values. In the Tcl implementation, they are equivalent to a division by 2+**+y.

7.1. Standard functions

Here is an overview of the various standard functions, organised in categories:

Table 4. Conversions between types of numbers

int(x)

Convert any number to a 32-bits integer (the value is truncated). For example: int(1.5) = 1, int(-1.5) = -1

wide(x)

Convert any number to a 64-bits integer (similar to int()

entier(x)

Convert any number to an arbitrary-range integer. Unlike int() and wide(), there is no limit to the range of the value

round(x)

Rounds the argument to the nearest integer number: round(1.5) = 2, round(-1.4) = -1

double(x)

Converts any number to a double-precision real number

Table 5. Arithmetic functions

abs(x)

Absolute value of x

ceil(x)

Returns the lowest integer value greater than x: ceil(1.5) = 2, ceil(-1.5) = -1

floor(x)

Returns the greatest integer value lower than x: floor(1.5) = 1, floor(-1.5) = -2

fmod(x,y)

Returns the fraction of a real value wrt a modulo: fmod(1.5,1) = 0.5, fmod(-1.5,1) = -0.5

(The integer modulo function is implemented via the operator %: 10%3 = 1)

hypot(x,y)

Length of the vector (x,y) (high precision, no overflow)

sqrt(x)

Square root of x

Table 6. Elementary mathematical functions

acos(x)

Arccosine of x (result in radians)

asin(x)

Arcsine of x (result in radians)

atan(x)

Arctangent of x (result in radians)

atan2(x)

Mathematical angle of the vector (x,y) (radians)

cos(x)

Cosine of x (x in radians)

cosh(x)

Hyperbolic cosine of x

exp(x)

Exponential function

hypot(x,y)

Hypothenuse of a rectangular triangle with sides x and y

log(x)

Natural logarithm of x

log10(x)

Logarithm with base 10 of x

pow(x,y)

x to the power y (alternatively: $x**$y)

sin(x)

Sine of x (x in radians)

sinh(x)

Hyperbolic sine of x

tan(x)

Tangent of x (x in radians)

tanh(x)

Hyperbolic tangent of x

Table 7. Random numbers

rand()

Return a random number

srand(x)

Seed the random number generator with seed x

Note If you need special functions like the gamma-function or Bessel functions of the first kind, check the math::special package in Tcllib.

8. Prefix notation

Sometimes, especially for ubiquitous simple expressions, it would be easier to use the prefix form. For instance: to double the value of x, you need to use:

set x [expr {2.0 * $x}]

Using a prefix notation would allow you to write:

set x [* 2.0 $x]

This is in fact possible by using the procedure form of the operations and functions defined by expr:

namespace import tcl::mathfunc::*
namespace import tcl::mathop::*
puts "x\tf(x)"
for { set i 0 } { $i < 100 } { incr i } {
    set x [* $i 0.05]
    puts "$x\t[* [sin $x] [exp [- $x]]]"
}

Whether you use this notation or expr and the infix notation is mostly a matter of taste. But they can be handy as a shorthand construction. Compare:

set sum [+ {*}$list_of_numbers]

to

set sum 0.0
foreach v $list_of_numbers {
    set sum [expr {$sum  +$v}]
}

9. Technical aspects

This section describes some more technical aspects of working with numbers in Tcl. Partly they are specific to Tcl, partly they are inherent to the floating-point arithmetic implemented in the hardware of your computer.

9.1. Exceptions and the special "numbers" Nan and Inf

Division by zero or calculating the root of a negative number are operations that cause problems: the outcome is undefined (at least within the realm of ordinary numbers). Two things can happen:

  • The expr command throws an exception:

    expr {sqrt(-1.0)}
    ==> domain error: argument not in valid range
  • The result is one of two special "numbers": Inf or NaN.

The first of these is short for "infinity" and it behaves as you would expect:

Inf * 2 = Inf, 2 / Inf = 0, etc.

The second is an odd one, even if it is useful from time to time. The abbreviation "NaN" means "not a number" and it has some strange properties:

  • Any arithmetic operation involving a NaN yields a NaN or throw an exception. So once you have `NaN’s in your calculations, they persist and may even spread.

  • The value NaN is unequal to any number, it is even unequal to itself:

    expr {NaN == NaN}
    ==> 0

One possible explicit use of `Nan’s is as a missing value in statistical calculations:

set sum   0.0
set count 0
foreach value $value {
    #
    # Avoid missing values
    #
    if { $value == $value } {
        set sum [expr {$sum + $value}]
        incr count
    }
}
puts "Mean value: [expr {$sum/$count}]"
Note Another popular choice for missing values is the empty string {}. Older versions of Tcl (before 8.5) do not support NaN and Inf.

9.2. Bracing your expressions

In all the code fragments shown here, we have used braces ({…​}) around the expressions. There is a simple reason for this: optimisation. If you do not brace your expressions, then the expression is interpreted (parsed) twice:

  • Once by Tcl itself to substitute all variable references and insert the results of any embedded commands

  • Once by expr to set up a structure for evaluating the various parts of the expression correctly.

If you brace your expressions, the first step is skipped and the second step is executed only once: the expr command then stores the structure for later reuse.

In very special cases, this may be a problem, for instance if the expression itself is dynamic, something along the lines of expr "$x $op $y" and op containing, say, a + if you want addition or - if you want subtraction. If you would surround this expression with braces, the value of $op would never be used in the parsing of the expression.

Here is a rather creative example:

# Sum the elements in a list
set list {1 2 3 4}
expr [join $list +]

As these cases are quite rare, the maxim is: 'Brace your expressions'.

In one particular case unbraced expressions could lead to the wrong result:

set countdown 10
while $countdown {
    puts "$countdown ..."
    incr countdown -1
}

Because the condition has no braces, it is evaluated by Tcl itself and the while command is passed a constant value: an infinite loop is born.

9.3. Octal numbers

As a left-over from the grey past, 'octal numbers' can sometimes be of use, but often present unexpected problems. A string consisting of digits that starts with a zero, such 012, is interpreted as an octal number. This causes trouble if it contains a digit 8 or 9 - expr will complain that it is not a valid octal number - but perhaps worse, a string that should be interpreted as a decimal number gets interpreted as an octal number and the resulting value is wrong: 012 is silently turned into the decimal value 10, instead of 12.

If you have to deal with such numbers, use the scan command to properly turn it into a decimal number:

scan "012" %d value
puts $value
==> 12

9.4. Security

Bracing expressions is generally recommended for a better performance, but there is another reason too. If you do not brace an expression, you might get into trouble with strings that contain commands:

set string {[exit]}
puts "The value of $string is: [expr $string]"

What happens? The expr command gets an expression "[exit]", parses it, notices that it contains square brackets, so it knows that a Tcl command must be run and runs that command - and therefore your program is terminated. This, of course, is one of the more innocent possibilities. If the expression was entered by a malicious user, it could contain very harmful code!

Bracing your expressions avoids this problem:

set string {[exit]}
puts "The value of $string is: [expr {$string}]"

Now, expr gets an expression that consists of the value of a variable. It does not need to evaluate a Tcl command, even though the value looks like a command, since that is not part of the expression!

Note Concerns for the security of your applications are best addressed by using a variety of techniques, such as the use of a 'safe interpreter' and the catch or try commands.