3

I'm trying to plot a normalized sinc function with PGFPlots/TikZ. As this is somewhat tricky because of the limiting value at 0, I used a suggestion provided by percusse.

Here's a minimal example (note that I've renamed the function to sincf due to conflicts with the rest of the document and inserted an approximation of Pi to normalize):

\documentclass{scrreprt}

\usepackage{pgfplots}
\pgfplotsset{compat=1.12}


\begin{document}
\begin{tikzpicture}

% https://tex.stackexchange.com/a/235009
\pgfmathdeclarefunction{sincf}{1}{%
        \pgfmathparse{abs(#1)<0.01 ? int(1) : int(0)}%
        \ifnum\pgfmathresult>0 %
            \pgfmathparse{1}%
        \else%
            \pgfmathparse{sin(3.14159*#1 r)/(3.14159*#1)}%
        \fi%
}

    \begin{axis}
        \addplot {sincf(\x)};
    \end{axis}

\end{tikzpicture}
\end{document}

However, I'm getting two errors which I can't figure out:

! Missing = inserted for \ifnum.
<to be read again>
Y
l.23 \addplot plot (\x,{sincf(\x)});
I was expecting to see `<', `=', or `>'. Didn't.

! Missing number, treated as zero.
<to be read again>
Y
l.23 \addplot plot (\x,{sincf(\x)});
A number should have been here; I inserted `0'.
(If you can't figure out why I needed to see a number,
look up `weird error' in the index to The TeXbook.)

Using MikTeX 2.9

2

3 Answers 3

4

pgfplots uses library fpu for better precision by default, which has a different internal number format. Therefore \pgfresult is not 1 or 0 anymore and \ifnum cannot be used.

\tikzset{fpu}
\pgfmathparse{int(0)}\show\pgfmathresult
\pgfmathparse{int(1)}\show\pgfmathresult

The lines show 0Y0 for zero and 1Y1 for one.

In this case, the whole math can be put in one \pgfmathparse expression:

\documentclass{scrreprt}

\usepackage{pgfplots}
\pgfplotsset{compat=1.12}

\begin{document}
\begin{tikzpicture}

    % http://tex.stackexchange.com/a/235009
    \pgfmathdeclarefunction{sincf}{1}{%
        \pgfmathparse{(abs(#1)<0.01) ? 1 : sin(pi*#1 r)/(pi*#1)}%
    }

    \begin{axis}
        \addplot {sincf(\x)};
    \end{axis}

\end{tikzpicture}
\end{document}

Result

4
  • Short, but very detailed, precise and quick answer. Thanks a lot! So if I get it right, 0Y0 is an internal representation of fpu which can't be used with \ifnum. But then why did the code by percusse work? Is it because \draw plot doesn't use fpu in contrast to \addplot? Aug 21, 2015 at 18:23
  • @user2009388 Exactly. Aug 21, 2015 at 18:35
  • Interesting. But then what does it use? Aug 21, 2015 at 19:23
  • @user2009388 The default mathematical engine, unless a different is set explicitly, examples: library fpu with changed internal format, library fixedpointarithmetic, based on package fp, ... Aug 21, 2015 at 19:24
3

Unfortunately, both of the existing answers have drawbacks.

Heiko Oberdiek's answer: it exactly undoes the fix in the linked question. In this case \addplot just happens to not evaluate the function at x=0, but if it does...

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\begin{document}

    \pgfmathdeclarefunction{sincf}{1}{%
        \pgfmathparse{(abs(#1)<0.01) ? 1 : sin(pi*#1 r)/(pi*#1)}%
    }
    \pgfmathparse{sincf(0)}

\end{document}

you got an error.

Cryptc's answer works, but only if the FPU is currently enabled and the output format is float.

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\begin{document}

\pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
\pgfmathparse{1}  % stores 1.0000000000 to \pgfmathresult
\pgfmathfloattofixed{\pgfmathresult}  % \pgfmathfloattofixed, as with other \pgfmathfloat... functions, expect input in low-level PGF FPU representation

\end{document}

Error.


One way I can find to convert anything to an int is to round-trip through PGF's FPU:

\documentclass{article}
\usepackage{tikz}
% don't need fpu library!
\begin{document}

% works with both internal representation and "normal" representation

\pgfmathfloatparsenumber{1Y1.0e6]}
\pgfmathfloattoint\pgfmathresult
% now \pgfmathresult is always an integer

\pgfmathfloatparsenumber{1000000}
\pgfmathfloattoint\pgfmathresult

\end{document}

You can try \pgfmathtruncatemacro, but this will silently return the wrong error if the FPU is enabled and output format is float!


So for this problem, the fix is

\documentclass{scrreprt}

\usepackage{pgfplots}
\pgfplotsset{compat=1.12}

\begin{document}

\pgfmathdeclarefunction{sinc}{1}{%
    \pgfmathparse{abs(#1)<0.01 ? 1 : 0}%
    % ======== add these 2 lines ========
    \pgfmathfloatparsenumber\pgfmathresult
    \pgfmathfloattoint\pgfmathresult
    % ======== end ========
    \ifnum\pgfmathresult>0 %
        \pgfmathparse{1}%
    \else%
        \pgfmathparse{sin(3.14159*#1 r)/(3.14159*#1)}%
    \fi%
}

\begin{tikzpicture}
    \begin{axis}
        \addplot {sinc(\x)};
    \end{axis}
\end{tikzpicture}

% ======== double check it works with normal \draw plot
\begin{tikzpicture}
    \draw (-1,0) -- (1,0);
    \draw[domain=0:0.5, samples=250] plot (\x,{sinc(20*\x)});
\end{tikzpicture}

\end{document}

works both with TikZ \draw plot and \addplot.


Alternatively, staying completely within the FPU, there's \pgfmathfloatifflags:

\pgfmathdeclarefunction{sinc}{1}{%
    \pgfmathparse{abs(#1)<0.01}%
    \pgfmathfloatparsenumber\pgfmathresult
    \pgfmathfloatifflags{\pgfmathresult}{1}{% "less than" is true
        \pgfmathparse{1}%
    }{% "less than" is false
        \pgfmathparse{sin(3.14159*#1 r)/(3.14159*#1)}%
    }%
}

Also a bit faster.


Another alternative, although specific to this question, is

\pgfmathdeclarefunction{sinc}{1}{%
    \pgfmathparse{(#1==0 ? 1: sin(3.14159*#1 r))/(#1==0 ? 1: 3.14159*#1)}%
}

(just stare at the code for a while you'll see why it works.)


I tried to make ifthenelse short-circuits, but evaluate-immediately-while-parsing behavior is quite hard-wired into the behavior of \pgfmathparse, so it seems difficult to fix.

3
0

Using a related answer about fpu formatting, I was able to use \ifnum in the case that my result was within the range of an int() [−16383.9998, +16383.9998]. The trick is to use \pgfmathfloattofixed on the \pgfmathresult.

\pgfkeys{/pgf/fpu=true}
\pgfmathparse{...do floating point math here (with an int() result)...}
\pgfmathfloattofixed{\pgfmathresult} % this allows the conversion with int()
\edef\tmp{\pgfmathresult}
\pgfkeys{/pgf/fpu=false}
\pgfmathsetmacro\iTmp{int(\tmp)} % convert with int()

\ifnum \iTmp>0
  ... do stuff ...
\fi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.