F5 states in K6091 that the BIG-IP iRules command set was developed from (Tool Command Language) base version 8.4.6, and that this base is used in all BIG-IP versions. So when an iRule compares a header to a number or does arithmetic on a value from a request, it follows Tcl 8.4.6's rules for reading numbers. Most of those rules are what you would . One of them is not, and it shows up in production as a rule that works all morning and fails at 08:00.
How Tcl 8.4 reads an integer
The Tcl 8.4 expr manual gives three ways to write an integer:
- digits with no prefix are decimal:
10is ten; - a leading
0makes the digits octal, base 8:010is eight; - a leading
0xmakes them hexadecimal:0x10is sixteen.
The octal rule is the trap. Octal digits run from 0 to 7, so 08 and 09 have the shape of an octal number and a digit octal does not have. Tcl does not fall back to decimal. The text 08 is simply not a number.
| Written | Read as | Value |
|---|---|---|
10 | decimal | 10 |
010 | octal | 8 |
0x10 | hexadecimal | 16 |
08 | not a number | none |
Where it bites: zero-padded values
Requests are full of zero-padded numbers: times, dates, codes, version fields. Say a header carries a time and an iRule takes the hour apart:
set hour [getfield "08:30" ":" 1]
if { $hour > 7 } {
pool day_pool
}
At 08:30 this condition is false. $hour holds 08, which is not a number, so > falls back to comparing text, and the text 08 sorts before 7 because the character 0 comes before 7. Arithmetic is louder about it: expr {$hour + 1} fails with "can't use invalid octal number as operand of +". At 07:30 both work, because 07 is a valid octal seven.
The fix is to read the value as decimal on purpose. scan with %d does exactly that, leading zero or not:
scan "08:30" "%d:%d" hour minute
# hour is 8, minute is 30
Two places Tcl reads a number
Tcl reads numbers in two different places, and they fail differently:
- A literal written inside the expression, such as
expr {$code == 08}. Tcl 8.4 checks literals when it compiles the expression, so08is an error before any part of the expression runs: "expected integer but got 08 (looks like invalid octal number)". - A value held in a variable, such as
set code 08; expr {$code == 8}. Here the value is read only when an operator needs a number. Arithmetic fails at that moment; a comparison quietly compares text instead, and$code == 8is simply false.
The second case is the dangerous one, because nothing is logged.
Doubles, and the digits you do not see
A number with a decimal point or an exponent is a double, a binary floating-point value. Tcl never treats a double as an integer, even a whole one: 3.0 == 3 is true, but 3.0 prints with its decimal point and the integer-only operator % refuses it. 1e3 reads as the double 1000.0.
Tcl 8.4 prints doubles with 12 significant digits, because its tcl_precision variable defaults to 12. That keeps output tidy and hides the rest of the value: expr {0.1 + 0.2} prints 0.3, yet expr {0.1 + 0.2 == 0.3} is 0, because the stored sum is 0.30000000000000004. Compare doubles with a tolerance, or work in integer units (milliseconds, cents) where you can.
Division follows the operand types. 10 / 3 is integer division and gives 3; 10 / 3. is double division and prints 3.33333333333. Integer division rounds toward negative infinity, so -7 / 2 is -4, not -3.
Large values
Integers in this engine are 64-bit, as Tcl 8.4 builds them on a 64-bit platform. Values from 2 to the power 63 up to 2 to the power 64 minus 1 are accepted and wrap around to negative numbers, so 0xFFFFFFFFFFFFFFFF reads as -1; anything larger is refused with "integer value too large to represent". A 32-bit build of Tcl 8.4 would read large values differently, so code that depends on values beyond 32 bits deserves a test on the system that runs it.
Try it
The number notation converter takes any value as you would write it and shows how it is read, its other notations, the literal and variable readings side by side, and how ==, eq, < and > compare it with a value you choose. The expression lab evaluates whole expressions and shows where each operand was read as a number or as text.
Sources
The Tcl version comes from F5's own knowledge base, the reading rules from the Tcl 8.4 manual, and the printing precision from the Tcl 8.4.6 source; every example above was run in Tcl 8.4.6.