# TikZ / pgfmath: pgfmathprintnumber if not a number

I need a function

1) x=-1 ---> ERROR
2) x < 0.01 --> 1000*x
3) x --> x else


The problem: The result should appear into \pgfmathprintnumber and that collapses in case 1), because the word 'ERROR' is not a number.

What could I do?

BTW: I wrote a lot with pgfmath in the main document and would like to stick with it instead of using something completely different here.

\documentclass[border=5mm, varwidth]{standalone}
\usepackage{tikz}
\begin{document}
\def\x{5} % works

\pgfmathdeclarefunction{Test}{1}{%%
\pgfmathparse{#1==-1}%
\ifnum\pgfmathresult=1 \pgfmathparse{"ERROR"} \else%
\pgfmathparse{#1<0.01}%
\ifnum\pgfmathresult=1%
\pgfmathparse{#1*1000}%
\else \pgfmathparse{#1}%
\fi%
\fi}%%

123: \pgfmathparse{Test(123)}
\pgfmathprintnumber[precision=2]{\pgfmathresult}
~pgfmathprintnumber works \\

0.0001: \pgfmathparse{Test(0.0001)}
\pgfmathresult ~works but strange result  \\
\pgfmathprintnumber[precision=2]{\pgfmathresult}
~ better rounded with pgfmathprintnumber \\

-1: \pgfmathparse{Test(-1)}
\pgfmathresult ~ works \\
%\pgfmathprintnumber[precision=2]{\pgfmathresult}
pgfmathprintnumber works not!
\end{document}

• \pgdmathparse is limited to the dynamic range of TeX length registers (10000: 0.0001 IIRC). \pgfmathparseFPU is much more accurate. May 21 '21 at 17:53
• @JohnKormylo It's a common misconception that FPU is more accurate, which is not true. It has a larger range, but the same limitations in terms of accuracy as the normal one. May 21 '21 at 18:07
• @JohnKormylo It doesn't. It stores mantissa and exponent separately. The format is <flags>Y<mantissa>e<exponent>. May 21 '21 at 19:15
• After further research I discovered that \pgfmathparseFPU is not a standard command. More to the point, it will not work without \usepgflibrary{fpu}, at which point \pgfmathparse also becomes more accurate. May 21 '21 at 22:54
• It is a pity that pgfmath does not convert the FPU register to hexadecimal. The first rule in applied math is "Never convert numbers to text while computing." May 22 '21 at 14:31

The command \pgfmathprintnumber internally calls \pgfmathfloatparsenumber which is part of the fpu library. The float parsing uses an error handler in case of invalid numbers which you can redefine.

The error handler is called /pgf/fpu/handlers/invalid number and has two arguments, the original input and the 'unreadable part'. Because there are two arguments the key code 2 args should be used to expose both #1 and #2. Now you can print for example the original input as part of the error handling:

\pgfkeys{/pgf/fpu/handlers/invalid number/.code 2 args={#1}}


However, this is not yet sufficient. According to the PGF manual the error handler is intended to correct the invalid input, after which the control is handed back to \pgfmathfloatparsenumber (and subsequently to \pgfmathprintnumber). This means that you can either use the various math functions within the handler to assign a new value to \pgfmathresult, or do nothing, then \pgfmathresult will be NaN as resulting from the original call to \pgfmathfloatparsenumber. The value of \pgfmathresult is then passed down to \pgfmathprintnumber and will be displayed.

Therefore, the definition above results in the output ERROR NaN, with ERROR printed through #1 in the error handler and NaN printed through further processing in \pgfmathprintnumber.

To get only the original input and not NaN in the output, some more modifications can be used. The idea is to modify the general code in pgfmath that prints the string NaN and turn this into a string that is conditional on a toggle. The error handler can turn the toggle to false, and the printing code can print the string if the toggle is true, and if it is false nothing is printed but the toggle is turned back to true to make sure NaN is printed when called from somewhere else.

Toggles and code patching functionality are provided by the etoolbox package.

MWE:

\documentclass[border=5mm, varwidth]{standalone}
\usepackage{tikz}
\usepackage{etoolbox}
\newtoggle{printnan}
\toggletrue{printnan}
\makeatletter
\patchcmd{\pgfmathfloatrounddisplaystyle@shared@impl}% patch the pgf command for printing NaN
{\hbox{NaN}}% find the following code
% replace by:
{\iftoggle{printnan}{\hbox{NaN}}% print normally if toggle is true
{\global\toggletrue{printnan}}}% print nothing and set toggle to true if it is false
{}{}
\makeatother
\makeatother
\pgfkeys{/pgf/fpu/handlers/invalid number/.code 2 args={#1\global\togglefalse{printnan}}}
\begin{document}
\def\x{5} % works

\pgfmathdeclarefunction{Test}{1}{%%
\pgfmathparse{#1==-1}%
\ifnum\pgfmathresult=1 \pgfmathparse{"ERROR"} \else%
\pgfmathparse{#1<0.01}%
\ifnum\pgfmathresult=1%
\pgfmathparse{#1*1000}%
\else \pgfmathparse{#1}%
\fi%
\fi}%%

-1: \pgfmathparse{Test(-1)}
\pgfmathresult ~ works \\
\pgfmathprintnumber[precision=2]{\pgfmathresult}
pgfmathprintnumber prints the input unchanged\\

123: \pgfmathparse{Test(123)}
\pgfmathprintnumber[precision=2]{\pgfmathresult}
~pgfmathprintnumber works \\

0.0001: \pgfmathparse{Test(0.0001)}
\pgfmathresult ~works but strange result  \\
\pgfmathprintnumber[precision=2]{\pgfmathresult}
~ better rounded with pgfmathprintnumber \\

\end{document}


Result: