Tcl Part I · Module 4 · Operators and Expressions
Arithmetic Operators
Module 3 established that arithmetic happens inside expr and that expr has an expression language of its own. This module goes through that language operator by operator, starting with the arithmetic you already use. Most of it behaves exactly as you expect, so this chapter spends its time on the parts that do not: how the remainder operator signs its result, why raising to a power associates the opposite way from everything else, and two cases where the answer is a whole number when you were expecting a fraction.
Foundation12 min readTclPart IOperatorsArithmeticexpr
Part I · Module 4 · Chapter 4.1 · Arithmetic Operators
1. The Six
puts "7 + 3 = [expr {7 + 3}]"
puts "7 - 3 = [expr {7 - 3}]"
puts "7 * 3 = [expr {7 * 3}]"
puts "7 / 3 = [expr {7 / 3}]"
puts "7 % 3 = [expr {7 % 3}]"
puts "2 ** 10 = [expr {2 ** 10}]"7 + 3 = 10
7 - 3 = 4
7 * 3 = 21
7 / 3 = 2
7 % 3 = 1
2 ** 10 = 1024Add, subtract, multiply, divide, remainder, power. Every expression is braced, for the reasons in Chapter 3.8.
2. Unary Minus and Plus
set a 5
puts "negate: [expr {-$a}]"
puts "negate twice: [expr {- -$a}]"
puts "unary plus: [expr {+$a}]"negate: -5
negate twice: 5
unary plus: 5Note the space in - -$a. Without it, --$a is not a decrement — Tcl has no ++ or -- operators at all. Counting up is incr, which is Chapter 3.9.
3. Division Is Covered Elsewhere
7 / 3 is 2 because both operands are whole numbers. That is important enough to have its own chapter — Chapter 3.10 — and this chapter does not repeat it.
The one-line version: if you want a fraction, make one operand a real number before dividing.
4. The Remainder, Including Negatives
% gives what is left after whole-number division:
puts "7 % 3 = [expr {7 % 3}]"7 % 3 = 1With negatives it is less obvious, so here is the complete table rather than one example:
foreach pair {{7 3} {-7 3} {7 -3} {-7 -3}} {
lassign $pair a b
puts [format "%3s %% %3s = %3s %3s / %3s = %3s" \
$a $b [expr {$a % $b}] $a $b [expr {$a / $b}]]
} 7 % 3 = 1 7 / 3 = 2
-7 % 3 = 2 -7 / 3 = -3
7 % -3 = -2 7 / -3 = -3
-7 % -3 = -1 -7 / -3 = 2The remainder takes the sign of the divisor. -7 % 3 is 2, not -1.
This follows from Tcl rounding division toward negative infinity, which Chapter 3.10 §6 covers. C truncates toward zero instead and gives -1 here, so code translated from C needs checking.
% works on whole numbers only:
puts [catch {expr {7.5 % 2}} m]
puts $m1
can't use floating-point value as operand of "%"If you need the remainder of a real number, fmod() is in Chapter 4.8.
5. The Power Operator Has Two Surprises
It associates to the right. Every other arithmetic operator groups left to right. ** does not:
puts "2 ** 3 ** 2 = [expr {2 ** 3 ** 2}]"
puts "(2 ** 3) ** 2 = [expr {(2 ** 3) ** 2}]"
puts "2 ** (3 ** 2) = [expr {2 ** (3 ** 2)}]"2 ** 3 ** 2 = 512
(2 ** 3) ** 2 = 64
2 ** (3 ** 2) = 5122 ** 3 ** 2 is 2 to the power 9, not 8 squared. This matches ordinary mathematical notation, and it is the opposite of what you get by reading left to right.
Unary minus binds tighter than it does in most languages:
puts "-2 ** 2 = [expr {-2 ** 2}]"-2 ** 2 = 4Tcl computed (-2) ** 2, which is 4. Python and several other languages compute -(2 ** 2), which is -4.
6. Powers Follow the Integer Rule Too
puts "2 ** -1 = [expr {2 ** -1}]"
puts "2.0 ** -1 = [expr {2.0 ** -1}]"
puts "4 ** 0.5 = [expr {4 ** 0.5}]"2 ** -1 = 0
2.0 ** -1 = 0.5
4 ** 0.5 = 2.02 ** -1 is one half, which as a whole number is 0. Two whole-number operands give a whole-number answer, exactly as with division.
The fix is the same: make one operand real, or use sqrt() and the other functions in Chapter 4.8.
One more, because the error is clear and worth recognising:
puts [catch {expr {(-8) ** (1.0/3)}} m]
puts $m1
domain error: argument not in valid rangeRaising a negative number to a fractional power has no real answer, and Tcl says so rather than returning something meaningless.
7. Big Numbers Still Work
From Chapter 3.5 §8:
puts [expr {2 ** 64}]
puts [expr {3 ** 40}]18446744073709551616
12157665459056928801Whole-number arithmetic has arbitrary precision, so powers do not overflow. If you want the real-number version, write a real operand.
8. Common Mistakes
Expecting / to give a fraction. Chapter 3.10.
Expecting -7 % 3 to be -1. It is 2. §4.
Reading 2 ** 3 ** 2 left to right. It is right-associative. §5.
Expecting -2 ** 2 to be -4. Tcl negates first. §5.
Using % on a real number. Use fmod(). §4.
Looking for ++ or --. They do not exist. Use incr.
9. Exercises
Exercise 1 — the six. Compute, predict first: 9 + 4, 9 - 4, 9 * 4, 9 / 4, 9 % 4, 9 ** 2.
Exercise 2 — the sign table. Without running, fill in -9 % 4, 9 % -4 and -9 % -4. Then check with the §4 loop. Which rule did you have to apply?
Exercise 3 — wrap an index. Write one expression that turns any integer i into a value between 0 and 6, and test it with i equal to 3, 10, -1 and -8. You should not need an if.
Exercise 4 — two power surprises. Predict 3 ** 2 ** 2 and -3 ** 2, then check. Rewrite both with parentheses so that a reader does not need to know the rules.
Exercise 5 — fix a fraction. expr {5 ** -2} gives 0. Produce 0.04 in two different ways.
Exercise 6 — the remainder of a real number. expr {7.5 % 2} fails. Find the answer another way, then say why Tcl refuses the operator form.
10. Remember
- Six operators:
+ - * / % **, all insideexpr. - There is no
++or--. Useincr. - Whole-number division discards the fraction — Chapter 3.10.
%takes the sign of the divisor.-7 % 3is 2, where C gives -1.- That makes
$i % $nsafe for wrapping an index, including negativei. %rejects real numbers. Usefmod().**associates to the right:2 ** 3 ** 2is 512.- Unary minus binds tighter than
**:-2 ** 2is 4, not -4. - Parenthesise every power expression. Two surprises in one operator is enough.
2 ** -1is 0, by the same whole-number rule as division.
Next: the order Tcl applies all of these in, and the habit that makes the order stop mattering.
Where this fits
Part of the Tcl curriculum.
