Introducing Tcl 8.7 Part 7: numbers

Published

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.

References

  1. TIP 472: Add Support for 0d Radix Prefix to Integer Literals

  2. TIP 521: Floating Point Classification Functions

  3. fpclassify manpage

  4. mathfunc manpage