Skip to content
VLSI Mentor

Tcl Part I · Module 3 · Values, Variables, and Expressions

Numbers: How Tcl Decides Something Is Numeric

Values carry text, so before you do arithmetic on something that came from outside your script you often want to know whether it will work as a number. Tcl gives you a direct way to ask. This chapter covers that, and two traps that are worth more than the rest of the page put together: the obvious way to write the check quietly accepts an empty value, and a number written with a leading zero does not mean what it looks like.

Foundation13 min readTclPart IValuesNumbersValidation

Part I · Module 3 · Chapter 3.5 · Numbers: How Tcl Decides Something Is Numeric

1. Why Ask At All

A value is text until a command reads it as something else — Chapter 3.1. Arithmetic is one such reading, and it fails if the text is not numeric:

Azvya Education Pvt. Ltd.VLSI Mentor
fails.tcl
set field "n/a"
puts [expr {$field * 2}]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
can't use non-numeric string as operand of "*"

That error is good when it happens during development and bad when it happens overnight on line 40,000 of a data file. Often you want to look first and decide what to do.

2. string is

The command that asks:

Azvya Education Pvt. Ltd.VLSI Mentor
asking.tcl
puts [string is integer -strict "42"]
puts [string is double  -strict "1e-3"]
puts [string is integer -strict "n/a"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1
1
0

string is integer asks "would this work as a whole number?". string is double asks "would this work as a real number?".

3. What Each One Accepts

Worth running once so the categories are concrete:

Azvya Education Pvt. Ltd.VLSI Mentor
categories.tcl
foreach v {42 -7 1e-3 3.14 0x1F n/a 007} {
    puts [format "%-6s integer=%s double=%s" $v \
        [string is integer -strict $v] \
        [string is double  -strict $v]]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
42     integer=1 double=1
-7     integer=1 double=1
1e-3   integer=0 double=1
3.14   integer=0 double=1
0x1F   integer=1 double=1
n/a    integer=0 double=0
007    integer=1 double=1

Three things to take from that table:

  • Every integer is also a valid double. string is double is the broader question.
  • Scientific notation counts as a double, not an integer.
  • Hexadecimal counts as an integer. 0x1F passes, and expr reads it as 31.

4. The Trap: -strict Is Not Optional

Here is the most important thing on this page. Leave -strict off and the empty string passes:

Azvya Education Pvt. Ltd.VLSI Mentor
strict.tcl
puts "empty, no -strict: integer=[string is integer {}] double=[string is double {}]"
puts "empty, -strict:    integer=[string is integer -strict {}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
empty, no -strict: integer=1 double=1
empty, -strict:    integer=0

Without -strict, string is integer "" is 1.

Think about what that means for a guard. You write the check specifically to catch bad input. An empty field — a blank column, a missing value, a line that did not parse — is the most common bad input there is. And the check passes it.

And the empty value does not fail later either, necessarily — it may produce a wrong number instead of an error, which is worse:

Azvya Education Pvt. Ltd.VLSI Mentor
empty-arith.tcl
set field ""
puts "is integer (no -strict): [string is integer $field]"
puts [expr {$field + 0}]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
is integer (no -strict): 1
can't use empty string as operand of "+"

Here expr did catch it. You cannot rely on that for every command, and the check that said "this is an integer" was wrong regardless.

5. Whitespace Is Accepted

A smaller surprise, and useful to know when parsing columns:

Azvya Education Pvt. Ltd.VLSI Mentor
whitespace.tcl
set field "  42  "

puts "is double -strict: [string is double -strict $field]"
puts "arithmetic:        [expr {$field + 0}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
is double -strict: 1
arithmetic:        42

Surrounding spaces are tolerated by both the check and by expr. You do not have to trim before testing — though trimming anyway makes the value easier to print and compare.

6. The Leading Zero

Now the second trap, and this one silently changes answers.

Azvya Education Pvt. Ltd.VLSI Mentor
octal.tcl
puts "expr 010  = [expr {010}]"
puts "expr 0x1F = [expr {0x1F}]"
puts "expr 0b101 = [expr {0b101}]"
puts "expr 0o17 = [expr {0o17}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
expr 010  = 8
expr 0x1F = 31
expr 0b101 = 5
expr 0o17 = 15

010 is 8, not 10. A leading zero means octal — base 8.

This matters because zero-padded numbers are everywhere in real data: 007, 010, 0900. If such a field reaches expr, you get a wrong answer with no error at all, unless the digits happen to include an 8 or a 9:

Azvya Education Pvt. Ltd.VLSI Mentor
octal-trap.tcl
puts [catch {expr {08 + 1}} m]
puts $m
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1
missing operator at _@_
in expression "0_@_8 + 1";
looks like invalid octal number

So 010 gives a wrong answer silently, while 08 fails loudly — and Tcl's message even guesses the cause for you. That inconsistency is what makes it dangerous.

7. The Fix: scan

scan reads a number out of text in a base you choose, and %d means decimal:

Azvya Education Pvt. Ltd.VLSI Mentor
scan.tcl
puts [scan "010" %d]
puts [scan "007" %d]
puts [expr {[scan "010" %d] + 1}]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10
7
11

010 read as decimal is 10, as you wanted.

Two practical approaches when handling zero-padded fields:

  • scan $field %d when you know the field is meant to be decimal. Explicit and version-proof.
  • Strip leading zeros as text before arithmetic, if you prefer to keep the value as text until the end.

The point is to decide deliberately rather than let a leading zero decide for you.

8. Tcl Integers Do Not Overflow

A pleasant surprise after the two traps:

Azvya Education Pvt. Ltd.VLSI Mentor
big.tcl
puts [expr {2**62}]
puts [expr {9223372036854775807 + 1}]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4611686018427387904
9223372036854775808

That second number is one past the largest signed 64-bit integer, and Tcl computed it exactly. Tcl integers have arbitrary precision — they grow as needed rather than wrapping around.

If you have come from C or SystemVerilog, this removes a whole category of bug. It also means you cannot use overflow to detect a value being too large; if you need a range check, write one.

9. Common Mistakes

Omitting -strict. The check then passes empty input. §4. This is the one to remember.

Assuming a zero-padded field is decimal. §6. Use scan.

Testing with string is integer when you meant to allow decimals. 3.14 fails that check. Use double unless you specifically require a whole number.

Expecting integer overflow. §8. Tcl integers grow.

Testing the wrong thing. string is integer tells you the text would work as a number. It does not tell you the value is sensible — -999999 passes.

10. Exercises

Exercise 1 — build the table. Run the §3 loop and add these values: " 5 ", "", 1_000, +7, .5, 5.. Which surprised you?

Exercise 2 — break a guard on purpose. Write a check using string is integer without -strict, feed it an empty value, and confirm it reports the value as a valid integer. Then add -strict and confirm it now reports 0.

Exercise 3 — the octal trap. Predict the output of expr {010 + 010} before running it. Then produce the answer you actually wanted, using scan.

Exercise 4 — loud or silent? Explain in one sentence why 010 is more dangerous than 08.

Exercise 5 — a reusable guard. Write three lines that take a variable field and print either the value doubled, or the message not numeric: <value>. Test it with 42, 3.5, "" and n/a.

Exercise 6 — big numbers. Compute 2 to the power 100 and print it. Then say what a C program using a 64-bit integer would have produced.

11. Remember

  • string is integer and string is double ask whether text would work as a number.
  • Always write -strict. Without it the empty string passes every class, which breaks exactly the guard you were writing.
  • Every integer is also a valid double. Hexadecimal counts as an integer; scientific notation counts only as a double.
  • Surrounding whitespace is accepted by both the check and expr.
  • A leading zero means octal on this interpreter — 010 is 8 — and the behaviour has varied across releases.
  • Use scan $field %d to read a zero-padded field as decimal, everywhere and in every version.
  • 08 errors while 010 is silently wrong. The silent one is the problem.
  • Tcl integers have arbitrary precision and do not overflow.

Next: the other thing a value gets read as — true or false.

Where this fits

Part of the Tcl curriculum.