Skip to content
VLSI Mentor

Tcl Part I · Module 4 · Operators and Expressions

Numeric Comparison, and the == Trap

Comparison looks like the simplest thing in this module and contains the module's worst trap. The six operators behave exactly as you expect on numbers. The problem is that == does not only compare numbers. When its operands do not look numeric it quietly falls back to comparing text, which means the same operator in the same line of code can ask two different questions depending on what the data happens to contain that day. This chapter shows exactly when each happens; the next chapter gives you the operator that does not do this.

Foundation12 min readTclPart IOperatorsComparisonEquality

Part I · Module 4 · Chapter 4.3 · Numeric Comparison, and the == Trap

1. The Six Operators

Azvya Education Pvt. Ltd.VLSI Mentor
six.tcl
puts "10 == 10  = [expr {10 == 10}]"
puts "10 != 9   = [expr {10 != 9}]"
puts "10 <  9   = [expr {10 < 9}]"
puts "10 <= 10  = [expr {10 <= 10}]"
puts "10 >  9   = [expr {10 > 9}]"
puts "10 >= 11  = [expr {10 >= 11}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10 == 10  = 1
10 != 9   = 1
10 <  9   = 0
10 <= 10  = 1
10 >  9   = 0
10 >= 11  = 0

Each returns 1 or 0 — which, from Chapter 3.6, are values Tcl accepts as true and false. That is where almost every condition you write gets its value.

2. Numbers Compare as Numbers

The useful half first. == compares the numeric values, not the text:

Azvya Education Pvt. Ltd.VLSI Mentor
numeric.tcl
puts "10 == 10.0   = [expr {10 == 10.0}]"
puts "1e2 == 100   = [expr {1e2 == 100}]"
puts "0x10 == 16   = [expr {0x10 == 16}]"
puts "\" 5\" == \"5\"  = [expr {" 5" == "5"}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10 == 10.0   = 1
1e2 == 100   = 1
0x10 == 16   = 1
" 5" == "5"  = 1

All four are true, and in all four the text on the two sides is different.

10 and 10.0 are different characters and the same number. 1e2 and 100 likewise. 0x10 is hexadecimal for 16. And " 5" has a leading space that the numeric reading ignores — Chapter 3.5 §5.

This is normally exactly what you want. A measurement that arrives as 10.0 should equal a threshold written as 10.

3. The Trap

Now what happens when the operands are not numeric:

Azvya Education Pvt. Ltd.VLSI Mentor
strings.tcl
puts "\"abc\" == \"abd\" = [expr {"abc" == "abd"}]"
puts "\"abc\" == \"abc\" = [expr {"abc" == "abc"}]"
puts "\"abc\" <  \"abd\" = [expr {"abc" < "abd"}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
"abc" == "abd" = 0
"abc" == "abc" = 1
"abc" <  "abd" = 1

No error. == compared them as text instead.

So the rule is:

== compares numerically when both operands look like numbers, and compares as text when they do not.

One operator, two questions, and the data decides which one you asked.

4. Why That Is Dangerous

A comparison that changes meaning based on its input is a comparison you cannot reason about by reading it.

Azvya Education Pvt. Ltd.VLSI Mentor
changes-meaning.tcl
proc same {a b} {
    return [expr {$a == $b}]
}

puts "same 10 10.0     -> [same 10 10.0]"
puts "same abc abc     -> [same abc abc]"
puts "same 007 7       -> [same 007 7]"
puts "same v1.0 v1.00  -> [same v1.0 v1.00]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
same 10 10.0     -> 1
same abc abc     -> 1
same 007 7       -> 1
same v1.0 v1.00  -> 0

Four calls to one procedure, and it asked a numeric question twice and a text question twice. Nothing in the source tells you which.

Look at the last two in particular. 007 and 7 are reported as the same because both read as numbers. v1.0 and v1.00 are reported as different because neither does. If these were version strings, the first result is wrong; if they were counts, the first is right. The operator cannot know, and neither can a reader.

5. Ordering Has the Same Split

< and > behave the same way:

Azvya Education Pvt. Ltd.VLSI Mentor
ordering.tcl
puts "9 < 10          = [expr {9 < 10}]"
puts "\"9\" < \"10\"      = [expr {"9" < "10"}]"
puts "\"apple\" < \"banana\" = [expr {"apple" < "banana"}]"
puts "\"Apple\" < \"apple\"  = [expr {"Apple" < "apple"}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
9 < 10          = 1
"9" < "10"      = 1
"apple" < "banana" = 1
"Apple" < "apple"  = 1

The first two give the same answer for different reasons — one numerically, one because both read as numbers anyway.

Note the last line: uppercase sorts before lowercase, because text comparison uses character codes. That is worth knowing before you use < on anything human-readable.

6. When == Is the Right Choice

None of this means avoid ==. It means use it when you mean a numeric question:

Azvya Education Pvt. Ltd.VLSI Mentor
good-use.tcl
set retries 3
set limit 3

if {$retries >= $limit} {
    puts "limit reached"
}

set temperature 20.0
puts "at threshold: [expr {$temperature == 20}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
limit reached
at threshold: 1

Both are genuinely numeric comparisons, and the 20.0 against 20 case is exactly the flexibility you want.

Two cautions even then:

  • Do not use == on measured real numbers. 0.1 + 0.2 == 0.3 is false, and Chapter 4.10 is entirely about why.
  • Validate first if the input is untrusted. Chapter 3.11 covers the guard.

7. The Fix Is the Next Chapter

If you want a comparison that always asks the same question, Tcl has one: eq and ne compare as text and never switch.

That is Chapter 4.4, and it is short — the hard part was understanding why you need it, which is this chapter.

8. Common Mistakes

Using == on identifiers, names or codes. They may be numeric today. §4.

Assuming == is a string comparison because the operands are quoted. Quotes are grouping — Chapter 2.7 — and do not affect the comparison.

Using == on real numbers. Chapter 4.10.

Using < on human-readable text without thinking about case. §5.

Expecting an error when the operands are not numbers. There is none; it falls back silently. §3.

9. Exercises

Exercise 1 — the six. Predict and check all six operators with the values 7 and 7, then 7 and 8.

Exercise 2 — which question was asked? For each, say whether == compared numerically or as text, then check:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
expr {100 == 1e2}
expr {"100" == "100.0"}
expr {"abc" == "ABC"}
expr {"0x0A" == 10}
expr {"v2" == "v2"}

Exercise 3 — make it change meaning. Write a single expr {$a == $b} line. Choose values for a and b that make it true as a numeric comparison, then choose different values that make the same line do a text comparison. You have just demonstrated the trap.

Exercise 4 — the version-string bug. You are comparing release identifiers. Show a pair where == gives the answer you want and a pair where it does not. What property of the data decides?

Exercise 5 — ordering and case. Sort out, by prediction then by test, which of these are true: "B" < "a", "a" < "b", "10" < "9", 10 < 9.

Exercise 6 — state the rule. In one sentence, and without using the word "sometimes", say what == does.

10. Remember

  • Six operators: == != < <= > >=. Each returns 1 or 0.
  • == compares numerically when both operands look like numbers, so 10 == 10.0, 1e2 == 100 and 0x10 == 16 are all true.
  • When they do not look numeric, it silently compares text instead.
  • So the data decides which question you asked, and a reader of the line cannot tell.
  • This breaks when data changes shape — identifiers that become all-digits start comparing numerically.
  • < and > split the same way, and text ordering puts uppercase before lowercase.
  • Use == when you mean a numeric question, and not on measured real numbers.
  • eq and ne are the operators that never switch. Next chapter.

Where this fits

Part of the Tcl curriculum.