Monday, April 5, 2010

Ordinal numbers

Ever want TeX to write an ordinal number correctly? There are a number of fairly complicated solutions on the web so I decided to write one that was easy to read and follow.
\def\ord#1{\begingroup
        \count@=#1\relax
        \the\count@
        \ifnum\count@>99
                \count0\count@
                \divide\count0 by100
                \multiply\count0 by100
                \advance\count@ by-\count0
        \fi
        \ifnum\count@>19
                \count0\count@
                \divide\count0 by10
                \multiply\count0 by10
                \advance\count@ by-\count0
        \fi
        \ifcase\count@ th%
                \or st%
                \or nd%
                \or rd%
                \else th%
        \fi
\endgroup}
Note that this requires @ to be a letter, so \makeatletter or \catcode`@11. The first \ifnum block mods out by 100 since that plays no role in determining the ordinal suffix. The next block mods out by 10 if \count@ is at least 20. This is because 10th through 19th use th but 20th through 99th only examine the ones digit. Lastly, if the one's digit is 0, 4, 5, 6, 7, 8, or 9, (or the tens digit is 1) use th, otherwise use st, nd, or rd as appropriate. Using old-style superscripts requires a trivial modification.

Default units

In the previous post, I mentioned that when changing font sizes, there is a neat hack that allows the font size macros to have default units. That is the subject of today's brief post. The setup is you want to devise a macro that sets a dimension register to a particular value. For example,
\newdimen\foo
\newcommand*\set[1]{\foo#1\relax}
Now if you know that most of the time the units used are in points—as is the case with the font size macros—you can add the units in the \set macro,
\newcommand*\set[1]{\foo#1 pt\relax}
This works unless you want to sometimes specify your own units. At this point, we can use the \afterassignment primitive and a macro with a delimited argument to scoop up the default units. This is exactly what the \@defaultunits macro does.
\def\@defaultunits{\afterassignment\remove@to@nnil}
\def\remove@to@nill#1\@nnil{}
We can change our \set macro to be
\newcommand*\set[1]{\@defaultunits\foo#1 pt\relax\@nnil}
Let's take a look to see what this does in two cases.
\set{10}
\set{10ex}
In the first case, \set expands which causes \@defaultunits to expand and the \afterassignment is set to \remove@to@nnil. Next, \foo 10 pt\relax causes the dimension register specified by \foo to be set to 10pt and now \remove@to@nnil expands which gobbles up the \@nnil and \set is finished.

In the second case, \@defaultunits expands and \afterassignment is set as before. This time, \foo 10ex causes the dimension register to be set to 10ex and \remove@to@nnil expands which gobbles the pt\relax\@nnil and \set is finished. The \relax is more crucial if the register being set is a skip register rather than a dimension register since the pt by itself is not sufficient for the assignment but the \relax causes TeX to stop looking for more skip specification tokens. Less brief than I intended.