Introducing Tcl 8.7 Part 7: numbers
This is the seventh in a series of posts about new features in the upcoming version 8.7 of Tcl. This post deals with some additional facilities, albeit minor, in handling of numbers.
To take Tcl 8.7 for a spin, you can download a pre-alpha binary for your platform. Alternatively, you can build it yourself from the
core-8-branch
branch in the Tcl fossil repository.
Introduction of 0d
as a decimal radix prefix
Tcl 8.6 recognized the use of 0b
, 0o
and 0x
(and upper case variants) as binary, octal and hexadecimal radix prefixes respectively. These were recognized in numeric literals, expr
and format
commands etc. Tcl 8.7 adds recognition of 0d
as a decimal radix prefix.
This makes Tcl's support for radix prefixes consistent. In addition, it simplifies treatment of decimal numbers from an external source such as a CSV file. For example, consider the fragment below that prints the increments an input number:
% set val [gets stdin] ; expr {$val * 2}
0123
166
Most people would be surprised by the result which is a consequence of Tcl treating 0123
as a octal number. Fixing this in Tcl 8.6 requires the use of scan
to ensure the input is treated as decimal.
% set val [gets stdin]
0123
0123
% scan $val %d val
1
% puts [expr {2*$val}]
246
In Tcl 8.7, we can force interpretation of the input as decimal a little more simply with the 0d
prefix.
% set val 0d[gets stdin] ; expr {$val * 2}
0123
246
No, not a earth-shattering feature but nice to have the consistency and Tclers have a fetish for consistency!
Classifying floating point numbers
The other addition related to numerics is ability to classify floating point numbers. Personally, I have never found a need for these but then again I have zero experience with serious numerical computation which is where I imagine these would be handy.
I assume the reader knows what the terms normal, subnormal, finite, infinite mean. If not, it's unlikely you will need to use these :-)
TIP 521 defines one new command, fpclassify
and six additional functions to in the ::tcl::mathfunc
namespace.
The fpclassify
command returns a value that indicates the class to which a floating point number belongs. This class may be one of zero
, normal
, infinite
, subnormal
and nan
.
The first four are easy enough to demonstrate.
% fpclassify 0
zero
% fpclassify 1.0
normal
% fpclassify [expr {1.0/0.0}]
infinite
% fpclassify 5e-324
subnormal
The nan
classification cannot currently be generated at the script level via a mathematical computation because an expression such as expr 0.0/0.0
generates an error (by design). TIP 520 proposes changing this to return a NaN
instead but that TIP is still pending for acceptance into 8.7. Nevertheless, NaN
can still occur in values, those passed from external C libraries for example. For demonstration purposes, we resort to some bit twiddling.
% binary scan [binary decode hex 7ff0000000000001] Q dbl_nan
1
% set dbl_nan
NaN(1)
% fpclassify $dbl_nan
nan
Related to this classification, Tcl 8.7 adds five functions to the ::tcl::mathfunc
namespace - isfinite
, isinf
, isnan
, isnormal
, issubnormal
- that return booleans that reflect whether the passed value falls into that category.
% expr {isnan($dbl_nan)}
1
% expr {isfinite(1.0/0.0)}
0
% expr {isinf(1.0/0.0)}
1
One additional function, isunordered
takes two values and returns a true boolean value if the two values cannot be compared. In particular, consider comparison with Inf
or NaN
.
% expr {isunordered(Inf,0)}
0
% expr {isunordered($dbl_nan,0)}
1
% expr {isunordered($dbl_nan,$dbl_nan)}
1
Note the last example where NaN
is not comparable to even itself.