14

If the need for random generators first arises when writing code for the tikzpicture environment, as it did in my case, it is natural to end up looking for recipes in Section 89 of Tantau's TikZ Manual. With expl3 L3fp's rand and randint functions available to me as of yesterday - better late than never!, I had reason to re-write my little toolbox containing a small number of document commands and associated cs_ functions to replace pgfmath with l3fp. After some tweaking, the toolbox does everything it did before except better, as detailed in the MWE below.

The question I have is the following: what use might there be for the pgfmath-variant of my toolbox? Are there circumstances where only pgfmath will do? The immediate issue is that of rand and randint vs. random (0, 1, or 2 arguments) and the associated methods of setting seed values. However, the wider scope of the question concerns the use of the many other functions common to floating point expressions in l3fp and mathematical expression in pgfmath.

\documentclass{article}
% RN. 9 April 2017
% BRIEF DESCRIPTION:
%    1. \myRandomNumberLThreeFP uses rand() and randint() provided by Expl3's
%       L3fp package. 
%    2. \myRandomNumberPGFMATH uses the random function provided by pgfmath. 
%    ADVANTAGES of expl3 fp over pgfmath are:
%    - Familiar syntax: for example ...
%      (1)  Allows the use of the familiar \int/fp_set/gset:Nn ... syntax to save 
%           a value to a variable, in place of the awkward 
%           \pgfmathsetmacro{<variable>}{random()}, etc.
%      (2)  pgfmath does not allow grouping in \cs_ functions - how then do I control 
%           local vs. global scope issues for the other variables resident in the 
%           same function?,  
%      (3)  ignores the check-declarations argument: \l_rn_randomInteger_int and 
%           \l_rn_randomInteger_fp are nowhere declared,
%      (4)  flags compile ERROR if \int_use:N or \fp_use:N is used when printing 
%           variables;
%    - Accuracy:
%      (1)  integers range -(10e16-1) to +(10e16-1) = 9,999,999,999,999,999 as 
%           opposed to (2e31-1) = 2,147,483,647 for pgfmath. 
%           NOTE: why then the "Number too big" error which persists even with all 
%           references to pgfmath removed?, 
%      (2)  reals have 16 decimal digits as opposed to 5 decimal digits for pgfmath.
%=======================
\usepackage[check-declarations]{expl3}
\usepackage{xparse}
\usepackage{pgfmath}
%-----------------------
\ExplSyntaxOn

\int_new:N \g_rn_randomInteger_int

%\NewDocumentCommand\myRandomNumberLThreeFP{O{1}O{9999999999999999}}  %  "Number too big" error ?
\NewDocumentCommand\myRandomNumberLThreeFP{O{1}O{2147483647}}
  {
    \rn_randomInt_LThreePFG:nn {#1}{#2}
    randint:~\int_use:N \g_rn_randomInteger_int ,~
    rand():~\fp_eval:n {rand()} \\
  }

\cs_new:Npn \rn_randomInt_LThreePFG:nn #1#2
  {
\group_begin:
    \int_gset:Nn \g_rn_randomInteger_int {\fp_eval:n {randint(#1,#2)}} 
\group_end:
  }    

\NewDocumentCommand\myRandomNumberPGFMATH{O{1}O{2147483647}}
  {
%\group_begin:
    \rn_randomInt_PGFMATH:nn {#1}{#2}
    random:~\l_rn_randomInteger_int,~
    \pgfmathsetmacro{\l_rn_randomFloatingPoint_fp}{random()}
    random():~\l_rn_randomFloatingPoint_fp \\
%\group_end:
  }

\cs_new:Npn \rn_randomInt_PGFMATH:nn #1#2
  {
%\group_begin:
    \pgfmathsetmacro{\l_rn_randomInteger_int}{random(#1,#2)}  
%\group_end:
  }    

\ExplSyntaxOff
%-----------------------
\begin{document}
    1.~Expl3~L3fp Package:\\
  \myRandomNumberLThreeFP 
  \myRandomNumberLThreeFP 
  \myRandomNumberLThreeFP 

    2.~pgfmath:\\
  \myRandomNumberPGFMATH
  \myRandomNumberPGFMATH
  \myRandomNumberPGFMATH
\end{document}
  • 2
    \group_begin: \int_gset:Nn .... \group_end:? What's the reason for the \int_gset if you use a group there (or vice versa?) – user31729 Apr 9 '17 at 9:46
  • Thanks for the heads-up about the random functions in l3fp. I've been waiting for those. – Loop Space Apr 9 '17 at 18:07
  • @Christian Hupfer I maintain a .sty file of definitions of scratch variables that I use throughout all my code. To ensure their local scope it is my routine for \cs_ functions to sandwich all code between \group_begin: ... \group_end: even with only a single variable (with global scope) present. If then in a later version of that function a scratch variable is required grouping won't be accidentally forgotten. As always, there is the possibility of me completely misunderstanding the issue, happens frequently, in which case I look forward to be enlightened. – Reinhard Neuwirth Apr 9 '17 at 22:52
17

The design aims of the two systems have some differences which may be pros or cons, depending on your requirements. The pgfmath engine is non-expandable and requires only classical TeX primitives. On the other hand, l3fp works by expansion and requires the extended primitive set mandated by LaTeX3: e-TeX and \pdfstrcmp or an equivalent (see also below).

Key considerations are

  • pgfmath uses TeX dimen registers for calculations. This limits accuracy and means that no internal value can exceed ±16383.99999. On the other hand, l3fp uses integer calculations (\numexpr) internally but with algorithms which allow 16-digit accuracy (the target is IEEE754 compliance). There is of course a trade-off here in terms of speed. (Note that pgf does allow swapping to a more accurate FPU depending on the nature of the work required.)
  • pgfmath works using assignments whereas l3fp works by pure expansion. Expandable calculations are useful in contexts where TeX 'requires a number' but do mean some operations are not possible. In particular, pgfmath will allow a box setting operation to be places inside an expression, whereas in l3fp only the measurement of a previously set box is allowable.
  • The range of functions implemented in pgfmath is larger than that in l3fp. For example, pgfmath covers arrays and hyperbolic trigonometry, neither of which is currently available in l3fp. (See the 'to do' list in interface3.)
  • Random numbers in l3fp require engine support whereas they are implemented directly in pgfmath. At present, this means that random values are unavailable in XeTeX (it lacks the required primitive), though that may change at some stage in the future. (It is possible to implement expandable random values without primitive support but this would be slow and overall not a sensible balance of effort and outcome.)
  • What syntax do I use for \pdfsetrandomseed. I noticed that both \int_use:N \pdfrandomseed and \int_eval:n {\pdfrandomseed}will print the current seed, but have been unsuccessful setting the seed. Mimicking pgfmath and using \pdfsetrandomseed{...} does not do the trick. – Reinhard Neuwirth Apr 10 '17 at 1:45
  • 1
    @ReinhardNeuwirth Something like \pdfsetrandomseed 1234 is required: it requires primitive count-setting syntax. That said, we will be looking at adding our own interfaces for this. – Joseph Wright Apr 10 '17 at 6:05
  • I had tried that option, unpromising as it seemed, and weird stuff happens. Anyway, no hurry, I will not be generating repeatable sequences until I know how to set the seed. – Reinhard Neuwirth Apr 10 '17 at 6:56
  • 1
    @ReinhardNeuwirth I'm not sure what you mean about 'weird stuff', something like \pdfsetrandomseed 1234 \showthe\pdfrandomseed happily works for me and shows the seed is 1234. – Joseph Wright Apr 10 '17 at 7:03
  • There must be something else I am doing wrong. pdfsetrandomseed <any number> produces a Number too big error, which makes absolutely no sense, hence "weird". I'll persist and if unsuccessful probably best to make it a question accompanied by an MWE. – Reinhard Neuwirth Apr 10 '17 at 8:12

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.