LaTeX3 gets a new FPU

For a while now we’ve been promising to update the floating point functions in LaTeX3 to provide an expandable approach to calculations. We are now making good progress in swapping the old code for the new material. There are still a few things to be decided, but the general plan looks pretty clear.

The new code lets you parse floating point expressions in the same way you can for integers. So you can write

\fp_eval:n { 10 / 3  + 0.35 }

and get the output

3.683333333333333

without needing to worry about working with dimensions. Even more usefully, there are a range of built in mathematical functions, so things like

\fp_eval:n { sin ( pi / 3 ) }

gives

0.8660254037844386

as you’d want.

You might pick up from the above output that the new FPU works to a higher accuracy than the old one: 16 significant figures. That’s been done without a bad performance hit: great credit goes to Bruno Le Floch for the work on this. Of course, you don’t have to have 16 figure output: the code includes rounding functions and various display options, so for example

\fp_eval:n { round ( sin ( pi / 3 ) , 3 ) }

At the moment you’ll have to grab the latest development code to get this new version as part of expl3, but it will be in the next CTAN snapshot some time in June. You might also find that not every function you want implemented is available: for example, there are currently no inverse trigonometric functions. Those are all on the to do list, but there not needed for typesetting so may be a little while.

Fixed point calculations in TeX

It’s well-known that TeX is good at integer arithmetic, and does not provide any primitive functions for real number calculations. Experienced TeX programmers will know that you can make use of dimen registers to do real number calculations at speed, at the cost of accuracy (as TeX truncates dimensions at five decimal places). However, this is not exactly ideal and can be a bit awkward to use.

An alternative for LaTeX users is to use the fp package (which has been around since LaTeX 2.09). This is a powerful package, but can be rather slow. Part of the reason is that it allows 18 digits either side of the decimal point: a wide range of numbers, but almost certainly overkill in most cases. For applications that really need large numbers (such as pgfplots) input using floating points (such as 1.23e20) is probably better. Floating point calculations make life a bit more complex again, but can be done in TeX (see for example pgfmath).

For LaTeX3 I’ve been working on what would be a sensible approach: real number functions are needed in various places. At the moment, the plan is to support fixed-point numbers with nine digits either side of the decimal place, so up to 999999999.999999999. That should be enough for most application, and at some stage supporting floating-point numbers might well be added. To date there is only basic arithmetic (add, subtract, multiply an divide) in the new code, but the plan is to add trigonometric and logarithmic functions. I’m sure that there will be other functions to add: I’ll be interested to see what is asked for.

One area that I’ve been working is overall performance compared to the fp package. The biggest single gain is of course moving from 18 plus 18 digits to 9 plus 9, which makes quite a big difference on it’s own. However, there are various places inside the code where there are opportunities to save time. First, as LaTeX3 requires ε-TeX I’ve exploited the \numexpr primitive where it makes a difference (mainly where it cuts down on the number of assignments needed). At the same time, there are places where using delimited macros is faster than actually doing mathematics! The exact performance gains depend on what exactly you are doing, but it is possible to draw some comparisons. Doing lots of repeated calculations it’s possible to get some feel for the difference between fp and the new LaTeX3 module. On my system 100 000 additions take 31.6 s with fp and 6.0 s with l3fp, while for 20 000 divisions it takes 64.8 s with fp and 4.1 s using l3fp. Quite some speed enhancement, and I think enough to justify using the new code!