expr(3tcl)
๐ expr(3tcl) โ Tcl Built-In Commands ๐
okay so bestie, this is the glow-up of a classic: expr(3tcl), the Tcl built-in that does math, logic, and a whole lot of string nonsense, all at once. she's iconic, she's powerful, and yes she has opinions about braces. let's get into it ๐ซถ
โจ NAME โจ
expr โ Evaluate an expression
๐ธ SYNOPSIS ๐ธ
expr arg ?arg arg ...?
๐ฆ DESCRIPTION ๐ฆ
So here's the vibe: expr concatenates all the *arg*s, sticks a space between each one, and calls the result an expression. Then it evaluates that expression and hands you back the value. Simple queen behaviour ๐
The operators you're allowed to use include a subset of the ones C lets you use. For the operators that Tcl and C share, Tcl gives them the exact same meaning and the exact same precedence as their C counterparts. No cap.
The value of an expression is often a numeric result โ either an integer or a floating-point value โ but it can also be a non-numeric value, because she contains multitudes. For example, this expression:
expr 8.2 + 6
evaluates to 14.2. Slay. โจ
Where expressions differ from C expressions is the way operands get specified. Expressions also support non-numeric operands, string comparisons, and some extra operators that C simply does not have (sorry to C).
When the result of an expression is an integer, it comes out in decimal form. When the result is a floating-point number, it comes out in the form produced by the %g format specifier of format. That's the tea. ๐ต
At any point in the expression โ except inside double quotes or braces โ the character # starts a comment. That comment runs until the end of the line, or the end of the expression, whichever one shows up first. periodt.
๐ OPERANDS ๐
An expression is a combination of operands, operators, parentheses and commas, and honestly you can sprinkle whitespace between any of those elements, because whitespace is ignored. Every operand gets interpreted as a numeric value if that is at all possible โ she will try, bestie, she will always try.
Each operand takes one of these forms:
- A numeric value โ either an integer or a floating-point number. The first two characters of an integer can also be
0dfor decimal,0bfor binary,0ofor octal, or0xfor hexadecimal. (yes, she speaks every radix fluently ๐)
A floating-point number can take any of several common decimal formats. It may use the decimal point ., e or E for scientific notation, and the sign characters + and -. All of these are valid floating-point numbers: 2.1, 3., 6e4, 7.91e+16. The strings Inf and NaN, in any combination of case, are also recognized as floating point values. And if an operand doesn't have a numeric interpretation, it must be quoted with either braces or double quotes. no negotiating.
Digits in any numeric value may be separated with one or more underscore characters, "_". A separator is only allowed between digits โ it can't appear at the start of a numeric value, it can't appear between the leading 0 and the radix specifier, and it can't appear at the end of a numeric value. Here are some examples, look how pretty:
expr 100_000_000 100000000
expr 0xffff_ffff 4294967295
format 0x%x 0b1111_1110_1101_1011 0xfedb
expr 3_141_592_653_589e-1_2 3.141592653589
- A boolean value โ using any form understood by
string is boolean. - A variable โ using the standard
$notation. The value of the variable is the value of the operand. obvious but we said it anyway ๐ - A string enclosed in double-quotes โ backslash, variable, and command substitution all happen according to the rules for Tcl.
- A string enclosed in braces. โ the operand is treated as a braced value, according to the rule for braces in Tcl.
- A Tcl command enclosed in brackets โ command substitution is performed according to the command substitution rule for Tcl.
- A function call. โ this is a mathematical function like
sin($x), and its arguments can have any of the operand forms listed above. See MATH FUNCTIONS below for how mathematical functions are handled.
Important bestie alert: because expr parses and performs substitutions on values that Tcl has already parsed and substituted, it is usually best to enclose your expressions in braces, so you avoid that first round of substitutions by Tcl. do it. your future self says thank you. ๐
Below are some simple expressions where the value of a is 3 and the value of b is 6. The command on the left side of each line produces the value on the right side:
expr {3.1 + $a} 6.1
expr {2 + "$a.$b"} 5.6
expr {4*[llength {6 2}]} 8
expr {{word one} < "word $a"} 0
๐ฅ OPERATORS ๐ฅ
Some operators have both a numeric mode and a string mode. The numeric mode gets chosen whenever all the operands have a numeric interpretation. And when both readings are possible, the integer interpretation of an operand is preferred over the floating-point interpretation. So if you want string operations on arbitrary values, it's generally a good idea to use eq, ne, or the string command, instead of more versatile operators like ==. just saying. ๐
Unless otherwise specified, operators accept non-numeric operands. The value of a boolean operation is 1 if it's true, and 0 otherwise. Also see string is boolean. The valid operators โ most of which are also available as commands in the tcl::mathop namespace (see mathop(3tcl)) โ are listed below, grouped in decreasing order of precedence:
-+~!โ Unary minus, unary plus, bit-wise NOT, logical NOT. These operators may only be applied to numeric operands, and bit-wise NOT may only be applied to integers.**โ Exponentiation. Valid for numeric operands. The maximum exponent value that Tcl can handle, if the first number is an integer > 1, is268435455. ๐*/%โ Multiply and divide, which are valid for numeric operands, and remainder, which is valid for integers. The remainder has an absolute value smaller than the absolute value of the divisor, and it carries the same sign as the divisor.
When applied to integers, division and remainder can be thought of as partitioning the number line into a sequence of adjacent, non-overlapping pieces, where each piece is the size of the divisor. The quotient tells you which piece the dividend lies within, and the remainder tells you where within that piece the dividend lies. A consequence of this is that the result of โ-57 / 10โ is always -6, and the result of โ-57 % 10โ is always 3. math is math, bestie.
+-โ Add and subtract. Valid for numeric operands.<<>>โ Left and right shift. Valid for integers. A right shift always propagates the sign bit.<><=>=โ Boolean numeric-preferring comparisons: less than, greater than, less than or equal, and greater than or equal. If either argument is not numeric, the comparison is done using UNICODE string comparison, exactly like the string comparison operators below, which have the same precedence.ltgtlegeโ Boolean string comparisons: less than, greater than, less than or equal, and greater than or equal. These always compare values using their UNICODE strings (also seestring compare), unlike the numeric-preferring comparisons above, which have the same precedence.==!=โ Boolean equal and not equal.eqneโ Boolean string equal and string not equal.inniโ List containment and negated list containment. The first argument is interpreted as a string, and the second as a list.intests for membership in the list, andniis the inverse. ๐ง&โ Bit-wise AND. Valid for integer operands.^โ Bit-wise exclusive OR. Valid for integer operands.|โ Bit-wise OR. Valid for integer operands.&&โ Logical AND. If both operands are true, the result is1, or0otherwise. This operator evaluates lazily; it only evaluates its second operand if it must in order to determine its result. This operator evaluates lazily; it only evaluates its second operand if it must in order to determine its result. (yes she said it twice, she meant it twice, we're keeping it ๐)||โ Logical OR. If both operands are false, the result is0, or1otherwise. This operator evaluates lazily; it only evaluates its second operand if it must in order to determine its result.x ? y : zโ If-then-else, as in C. Ifxis false, the result is the value ofy. Otherwise the result is the value ofz. This operator evaluates lazily; it evaluates only one ofyorz. choose wisely bestie.
The exponentiation operator promotes types in the same way that the multiply and divide operators do, and the result is the same as the result of pow. Exponentiation groups right-to-left within a precedence level. Every other binary operator groups left-to-right. For example, the value of
expr {4*2 < 7}
is 0, while the value of
expr {2**3**2}
is 512. โจ
As in C, &&, ||, and ?: all feature โlazy evaluationโ, which means operands are not evaluated if they aren't needed to determine the outcome. For example, in
expr {$v?[a]:[b]}
only one of [a] or [b] is evaluated, depending on the value of $v. This is not true of the normal Tcl parser, so it's normally recommended to enclose the arguments to expr in braces. Without braces, like in expr $v ? [a] : [b], both [a] and [b] get evaluated before expr is even called. ๐ญ
For more details on the results produced by each operator, see the documentation for C.
๐งฎ MATH FUNCTIONS ๐งฎ
A mathematical function like sin($x) gets replaced with a call to an ordinary Tcl command living in the tcl::mathfunc namespace. So the evaluation of an expression like
expr {sin($x+$y)}
is the same in every way as the evaluation of
expr {[tcl::mathfunc::sin [expr {$x+$y}]]}
which in turn is the same as the evaluation of
โฆand bestie that sentence does not end here. part 2 is coming, don't you dare close the tab ๐
tcl::mathfunc::sin [expr {$x+$y}]
Okay so tcl::mathfunc::sin gets resolved exactly like the NAMESPACE RESOLUTION section in the namespace(3tcl) docs says. With the default value of namespace path, [namespace current]::tcl::mathfunc::sin or ::tcl::mathfunc::sin are the typical resolutions. iykyk ๐
Just like in C, a mathematical function can accept multiple arguments separated by commas. So expr {hypot($x,$y)} becomes tcl::mathfunc::hypot $x $y. Slay. See the mathfunc(3tcl) documentation for the math functions that are available by default. ๐
๐ธ TYPES, OVERFLOW, AND PRECISION ๐ธ
Internal floating-point computations are done using the double C type. When converting a string to a floating-point value, exponent overflow is detected and results in the double value of Inf or -Inf as appropriate. Floating-point overflow and underflow are detected to the degree supported by the hardware, which is generally fairly reliable. ๐
Conversion among internal representations for integer, floating-point, and string operands is automatic as needed. For arithmetic computations, integers are used until some floating-point number is introduced, after which floating-point values are used. For example, expr {5 / 4} returns 1, while
expr {5 / 4.0}
expr {5 / ( [string length "abcd"] + 0.0 )}
both return 1.25. A floating-point result can be distinguished from an integer result by the presence of either โ.โ or โeโ. So expr {20.0/5.0} returns 4.0, not 4. Periodt. ๐
โก PERFORMANCE CONSIDERATIONS โก
Where an expression contains syntax that Tcl would otherwise perform substitutions on, enclosing the expression in braces or otherwise quoting it so that it's a static value allows the Tcl compiler to generate bytecode for the expression, resulting in better speed and smaller storage requirements. This also avoids issues that can arise if Tcl is allowed to perform substitution on the value before expr is called. Not me letting Tcl substitute early. ๐ฅ
In the following example, the value of the expression is 11 because the Tcl parser first substitutes $b and expr then substitutes $a as part of evaluating the expression $a + 2*4. Enclosing the expression in braces would result in a syntax error as $b does not evaluate to a numeric value.
set a 3
set b {$a + 2}
expr $b*4
When an expression is generated at runtime, like the one above is, the bytecode compiler must ensure that new code is generated each time the expression is evaluated. This is the most costly kind of expression from a performance perspective. In such cases, consider directly using the commands described in the mathfunc(3tcl) or mathop(3tcl) documentation instead of expr. Main character energy: avoid runtime expr. ๐ฆ
Most expressions are not formed at runtime, but are literal strings or contain substitutions that don't introduce other substitutions. To allow the bytecode compiler to work with an expression as a string literal at compilation time, ensure that it contains no substitutions or that it is enclosed in braces or otherwise quoted to prevent Tcl from performing substitutions, allowing expr to perform them instead. โจ
If it is necessary to include a non-constant expression string within the wider context of an otherwise-constant expression, the most efficient technique is to put the varying part inside a recursive expr, as this at least allows for the compilation of the outer part, though it does mean that the varying part must itself be evaluated as a separate expression. Thus, in this example the result is 20 and the outer expression benefits from fully cached bytecode compilation.
set a 3
set b {$a + 2}
expr {[expr $b] * 4}
In general, you should enclose your expression in braces wherever possible, and where not possible, the argument to expr should be an expression defined elsewhere as simply as possible. It is usually more efficient and safer to use other techniques (e.g., the commands in the tcl::mathop namespace) than it is to do complex expression generation. Lowkey a vibe check for your Tcl. ๐
๐ EXAMPLES ๐
A numeric comparison whose result is 1:
expr {"0x03" > "2"}
A string comparison whose result is 1:
expr {"0y" > "0x12"}
A forced string comparison whose result is 0:
expr {"0x03" gt "2"}
Define a procedure that computes an โinterestingโ mathematical function:
proc tcl::mathfunc::calc {x y} {
expr { ($x**2 - $y**2) / exp($x**2 + $y**2) }
}
Convert polar coordinates into cartesian coordinates:
# convert from ($radius,$angle)
set x [expr { $radius * cos($angle) }]
set y [expr { $radius * sin($angle) }]
Convert cartesian coordinates into polar coordinates:
# convert from ($x,$y)
set radius [expr { hypot($y, $x) }]
set angle [expr { atan2($y, $x) }]
Print a message describing the relationship of two string values to each other:
puts "a and b are [expr {$a eq $b ? {equal} : {different}}]"
Set a variable indicating whether an environment variable is defined and has value of true:
set isTrue [expr {
# Does the environment variable exist, and...
[info exists ::env(SOME_ENV_VAR)] &&
# ...does it contain a proper true value?
[string is true -strict $::env(SOME_ENV_VAR)]
}]
Generate a random integer in the range 0..99 inclusive:
set randNum [expr { int(100 * rand()) }]
๐ SEE ALSO ๐
array(3tcl), for(3tcl), if(3tcl), mathfunc(3tcl), mathop(3tcl), namespace(3tcl), proc(3tcl), string(3tcl), Tcl(3tcl), while(3tcl)
๐ KEYWORDS ๐
arithmetic, boolean, compare, expression, fuzzy comparison, integer value
๐ COPYRIGHT ๐
Copyright ยฉ 1993 The Regents of the University of California. Copyright ยฉ 1994-2000 Sun Microsystems Incorporated. Copyright ยฉ 2005 Kevin B. Kenny <kennykb@acm.org>. All rights reserved.
That's the Tcl 8.5, expr(3tcl) page, bestie โ source/version: Tcl 8.5, expr(3tcl); date: not shown in this footer, iconic mystery. Periodt. ๐ โจ
๐ฆ where this page comes from
- ubuntu questing: tcl9.0-doc 9.0.2+dfsg-1
- ubuntu resolute: tcl9.0-doc 9.0.3+dfsg-1
- ubuntu stonking: tcl9.0-doc 9.0.4+dfsg-1
- Copyright (c) 1993 The Regents of the University of California
- Copyright (c) 1994-2000 Sun Microsystems, Inc
- Copyright (c) 2005 Kevin B. Kenny <kennykb@acm.org>. All rights reserved
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms