34

I am looking for a method to do minimal floating point / integer calculations in LaTeX, not for the purpose of package writing, but for the production of actual text.

Here is an example to illustrate. Given the following input text

 The experiment included running a battery of \bind{T}{21} tests
 on \bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
 However, since \bind{F}{37} values were defective, our successful 
 measurement rate per subject was \use{100*round((V-F)/T,2)}\%

it would be nice if was converted to:

 The experiment included running a battery of $21$ tests
 on $13$ subjects, for a total of $273$ expected values.
 However, since $37$ values were defective, our successful 
 measurement rate per subject was $86$\%.

Where \bind{C}{expression} defines a new constant C whose value is expression, and returns C, while \use{C} simply returns the value of C. Any tips?

I am aware of the spreadtab package and this question: this question, but I am looking for something more.

3

7 Answers 7

18

If you don't mind using luatex, then you could just let lua do the calculations. I use ConTeXt, but I believe that the luacode package in LaTeX provides similar functionality.

\startusercode
  round = global.math.round 
\stopusercode

\def\bind#1#2%
    {\usercode{#1 = #2}\use{#1}}

\def\use#1%
    {\usercode{global.context(#1)}}

\starttext
 The experiment included running a battery of \bind{T}{21.0} tests
 on \bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
 However, since \bind{F}{37} values were defective, our successful 
 measurement rate per subject was \use{100*round((V-F)/T,2)}\%
\stoptext

I am using usercode rather luacode so that my definitions (T=21 etc) do not pollute the global namespace.

EDIT: If you want the result to be in math mode, change global.context(...) to global.context.math(...) in the definition of \use

0
18

Another package to do floating point computations is l3fp. This package does not (yet) allow users to define new variables (but it supports pi, for instance, so in principle it shouldn't be too hard to extend it to support user-defined variables). A workaround is to go through the expression and replace all variables T, V, etc. by an internal variable \l__my_T_fp, \l__my_V_fp, etc., which holds the corresponding value.

This is done by the looping macro \__my_use:N, which reads one character at a time. If it is the end-marker \q_recursion_tail, then we reached the end of the expression, so exit the loop by skipping to \q_recursion_stop (done by \quark_if_recursion_tail_stop:n). If the variable \l__my_#1_fp is defined, then use it, otherwise leave the character itself for the l3fp functions to receive. Then recurse by calling \__my_use:N again.

The \my_set:nn function makes sure that \l__my_#1_fp is defined thanks to \fp_zero_new:c, then it sets its value.

\documentclass{article}
\usepackage{expl3, xparse}
\ExplSyntaxOn
\cs_new:Npn \__my_use:N #1
  {
    \quark_if_recursion_tail_stop:n {#1}
    \cs_if_exist_use:cF { l__my_#1_fp } {#1}
    \__my_use:N
  }
\cs_new:Npn \my_use:n #1
  { \fp_eval:n { \__my_use:N #1 \q_recursion_tail \q_recursion_stop } }
\cs_new_protected:Npn \my_set:nn #1#2
  {
    \fp_zero_new:c { l__my_#1_fp }
    \fp_set:cn { l__my_#1_fp } { \my_use:n {#2} }
  }
\NewDocumentCommand {\bind} { m m } { \my_set:nn {#1} {#2} \my_use:n {#1} }
\NewDocumentCommand {\use} { m } { \my_use:n {#1} }
\ExplSyntaxOff
\begin{document}
 The experiment included running a battery of \bind{T}{21} tests
 on \bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
 However, since \bind{F}{37} values were defective, our successful
 measurement rate per subject was \use{100*round((V-F)/V,2)}\%
\end{document}

I also fixed the last formula, which read (V-F)/T.

15

I have been extensively using the fp for this type of work. Here is a minimal for some functions, that can convert temperature units from one system to another. There are a lot of examples on this site that use the fp package. The code uses the siunitx package for formatting as well.

For example to translate degrees F to C you type \Ftoc{32.999}. Here is a minimal example.

\documentclass{article}
\usepackage{fp}
\usepackage{siunitx}
\gdef\numdec{3}

\begin{document}
%\SetConversion{C}{K}{273.15}
\def\CtoK#1{\FPadd\result{#1}{273.15}%
\FPround\result{\result}{2}%
\sisetup{%
fixed-exponent = 2,
scientific-notation = false}
 \num{\result}}


%% Convert Centigrate to Fahreneit
\def\CtoF#1{\FPdiv\resulta{9}{5}%
\FPmul\resultb{\resulta}{#1}%
\FPadd\resultc{\resultb}{32}%
\FPround\resultc{\resultc}{3}%
 \num{\resultc}%
\sisetup{%
fixed-exponent = 0,
scientific-notation = false}
}

%% Convert Fahreneit to Centigrade
\def\FtoC#1{\FPdiv\resulta{5}{9}%
\FPsub\resultb{#1}{32}%
\FPmul\resultc{\resultb}{\resulta}%
\FPround\resultc{\resultc}{\numdec}%
\sisetup{%
fixed-exponent = 0,
scientific-notation = false}
\num{\resultc}%
}

%% Convert Fahreneit to Rankine
\def\FtoRa#1{\FPmul\result{#1}{9}%
\FPdiv\result{\result}{5}%
\FPround\result{\result}{\numdec}%
\result%
}

%% Convert Kelvin to Rankine
\def\KtoRa#1{\FPadd\result{#1}{459.67}%
\FPround\result{\result}{\numdec}\result}

%% Convert Rankine to Celcius
\def\RtoC#1{\FPsub\result{#1}{459.67}% to fahreneit
\FtoC{\result}}


\FtoC{32.999}

\end{document}
7

I would use the math engine of pgf for it.

It would be much simpler if you would use macro names instead, e.g. \T instead of T. Then \bind could be defined as \newcommand\bind[2]{\expandafter\def\csname#1\endcsname{#2}#1} and \use would be \newcommand\use[1]{\pgfmathparse{#1}\pgfmathresult}. You would need to write \use{100*round((\V-\F)/\T,2)} then.

IMHO using characters only should be possible, but would requires the definition of the characters as PGF constants/functions. I not fully sure if this will work with your original syntax.

4
  • 1
    This will only work in the current scope, right? Perhaps that's a good thing. But I'm worried about cluttering the namespace with all those one-character control sequences. Apr 11, 2011 at 12:46
  • 1
    In particular, you redefine \S in this example.
    – Aditya
    Apr 11, 2011 at 13:16
  • Yes, you need to be careful with the macro names. Added a prefix would be much saver. I just wanted to show the principle. Apr 11, 2011 at 13:22
  • @Matthew: Yes, this will only be working the current scope. Except if you use \gdef instead of \def. Apr 11, 2011 at 13:23
6

For the sake of variety, here's a LuaLaTeX-based solution. It provides two LaTeX macros: \bind and \use. In addition, because Lua does not provided a built-in round function, it supplies an implementation of round, for use in a \use directive.

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}

%% The LaTeX macros "\bind" and "\use" invoke "\directlua{...}"
%% to perform their task(s).
\newcommand\bind[2]{\directlua{#1=#2;tex.sprint(#1)}}
\newcommand\use[1]{\directlua{tex.sprint(#1)}}

%% Define two auxiliary functions: "round2int" and "round"
\directlua{%
   function round2int ( x )
      return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
   end
   function round ( num , digits )
      return round2int ( num * 10^digits ) / 10^digits
   end
}

\begin{document}
The experiment included running a battery of \bind{T}{21} tests on
\bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
However, since \bind{F}{37} values were defective, our successful 
measurement rate per subject was \use{100*round((V-F)/V,2)}\%. More 
precisely, the successful measurement rate per subject was 
\use{100*round((V-F)/V,4)}\%.
\end{document}
5
  • I merged the two directives in one and use siunitx. Is there any warnings to do that ? : \NewDocumentCommand{\CLua}{ o % Affecte à une variable m % valeur ou calcul avec les variables définies o % unités pour siunitx }{% \IfValueT{#1}{\directlua{#1=#2}}% \IfNoValueTF{#3}{% \num{\directlua{tex.sprint(#2)}}% }{% \SI{\directlua{tex.sprint(#2)}}{#3}% }% }
    – Tarass
    Mar 20, 2018 at 18:05
  • @Tarass - Believe it or not, but I'm absolutely no good at LaTeX3-syntax. I can't tell what your macro does. Sorry.
    – Mico
    Mar 20, 2018 at 18:24
  • It's normal tex syntax. Never mind. Thank you.
    – Tarass
    Mar 20, 2018 at 19:24
  • @Tarass - Pardon me -- I should have written "I am unfamiliar with the \NewDocumentCommand syntax".
    – Mico
    Mar 20, 2018 at 19:36
  • Thank you anyway, I'm very glad with your answer. I just asked you by precaution.
    – Tarass
    Mar 20, 2018 at 19:40
4

Package xintexpr allows exact computations on arbitrarily big numbers and fractions and decimal numbers, and it allows definition of variables (with no \) for use in later \xintexpr, \xintfloatexpr or \xintiexpr.

It works purely internally to TeX, building upon the e-TeX extension \numexpr. It can do floating point evaluations according to the value set by \xintDigits:=N;. Default is with 16 digits of precision.

This answer illustrates the use of user-declared variables, which was introduced with release 1.1 of 2014/10/28. Thus, the implementation of \bind and \use is straightforward.

Allowed variable names are composed with letters, digits, underscores and are not allowed to start with a digit.

\documentclass{article}

\usepackage{xintexpr}% v1.1 or later

% If you use \bind with single letters T, S, etc...,
% they will not be allowed in dummy summations such
% as add(T^2, T=1..10) inside \xintexpr, \xintfloatexpr,... anymore.

% If \bind is used in an environment it obeys its scope and the
% remark above does not apply outside of the environment.

% This computes first argument as result of evaluation of second
% argument.
\newcommand*{\bind}[2]{\xintdefvar #1:=#2;\xinttheexpr #1\relax}

% The first argument specifies the number of digits
% to retain for fixed point output after decimal mark
% Default is 0, which means, round to nearest integer.
\newcommand*{\use}[2][0]{\xinttheiexpr [#1]#2\relax} 

\begin{document}
The experiment included running a battery of \bind{T}{21} tests
 on \bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
 However, since \bind{F}{37} values were defective, our successful 
 measurement rate per subject was
% Note: the original has (V-F)/T which appears to be a misprint.
\use{100*(V-F)/V}\%.

More precisely the
 success rate per subject was \use[2]{100*(V-F)/V}\%.

Another experiment had  \bind{F}{53} defective values and its
success rate per subject was \use[2]{100*(V-F)/V}\%.

\end{document}

user declared xintexpr variables

3

An easy and intuitive solution is to use the package calculator. It enables variables handling (definition, use and calculation) easily and without needing LuaTeX, nor eTeX.

To affect a value to a variable, you use \COPY{<value>}{\myVariable} (yes, it's upper case). You can then print/use your variable value as simply as \myVariable.

To do some calculation, you do \ADD{<a>}{<b>}{<command_that_is_a+b>} (the same with \SUBTRACT,\MULTIPLY,\DIVIDE... see manual p. 6 and after for more detail).

Below are shown three MWE that compute following output (twice the same sentence, with only a change in variables values):
MWE

  • MWE1: Intuitive and easy to implement (but does not follow your syntax).
    => good for those who want to do some floating point calculations in LaTeX.
  • MWE2: Implements the \bind{<letter>}{<value>} into MWE 1, which uses TeX macros (so less obvious to replicate for LaTeX-learners). It follows your syntax, except for the \use-command.
  • MWE3: [Work in progress] Follows all your syntax.

MWE1

This MWE changes your syntax. It is however easy to use and reuse, since it only loads a LaTeX-package and used macro are explicit. This might thus be adequate for people who wants to do "floating point calculations in LaTeX".

\documentclass{scrartcl}
    \usepackage{calculator}
    \usepackage{xspace}% xspace "smartely" adds or not a space at the end of the macro

    \newcommand*{\printExpectedValue}{%
        \MULTIPLY{\myT}{\myS}{\myV}% compute "V" value
        \GLOBALCOPY{\myV}{\myV}% makes "V" a global constant: needed to compute SucessfulRate
        $\myV$\xspace% print "V" in mathmode
    }

    \newcommand*{\printSucessfulRate}{%
        \SUBTRACT{\myV}{\myF}{\intermediateResultA}% does the intermediary calculation
        \DIVIDE{\intermediateResultA}{\myV}{\intermediateResultB}%
        \MULTIPLY{\intermediateResultB}{100}{\intermediateResultC}%
        \ROUND[0]{\intermediateResultC}{\mySucessfulRate}%
        $\mySucessfulRate$\%\xspace% prints result in math-mode
    }

\begin{document}
    \COPY{21}{\myT}% defines \myT
    \COPY{13}{\myS}% defines \myS
    \COPY{37}{\myF}% defines \myF
    The first experiment included running a battery of $\myT$ tests on $\myS$ subjects, for a total of \printExpectedValue expected values.
    However, since $\myF$ values were defective, our successful measurement rate per subject was \printSucessfulRate.

    \COPY{42}{\myT}% redefines \myT
    \COPY{17}{\myS}% redefines \myT
    \COPY{210}{\myF}% redefines \myT
    The second experiment included running a battery of $\myT$ tests on $\myS$ subjects, for a total of \printExpectedValue expected values.
    However, since $\myF$ values were defective, our successful measurement rate per subject was \printSucessfulRate.
\end{document}

MWE2

This MWE replicates the \bind{<letter>}{<value>} command. However, it therefor uses plain-TeX commands, which might be not indicated if you don't know what you are doing.

\documentclass{scrartcl}
    \usepackage{calculator}
    \usepackage{xspace}% xspace "smartely" adds or not a space at the end of the macro

    \newcommand*{\bind}[2]{%
        \expandafter\def\csname my#1\endcsname{#2}% defines the \myX macro
        $#2$\xspace%\prints the "X" value in math-mode
    }

    \newcommand*{\printExpectedValue}{%
        \MULTIPLY{\myT}{\myS}{\myV}% compute "V" value
        \GLOBALCOPY{\myV}{\myV}% makes "V" a global constant: needed to compute SucessfulRate
        $\myV$\xspace% print "V" in mathmode
    }

    \newcommand*{\printSucessfulRate}{%
        \SUBTRACT{\myV}{\myF}{\intermediateResultA}% does the intermediary calculation
        \DIVIDE{\intermediateResultA}{\myV}{\intermediateResultB}%
        \MULTIPLY{\intermediateResultB}{100}{\intermediateResultC}%
        \ROUND[0]{\intermediateResultC}{\mySucessfulRate}%
        $\mySucessfulRate$\%\xspace% prints result in math-mode
    }
\begin{document}
    The first experiment included running a battery of \bind{T}{21} tests on \bind{S}{13} subjects, for a total of \printExpectedValue expected values.
    However, since \bind{F}{37} values were defective, our successful measurement rate per subject was \printSucessfulRate.

    The second experiment included running a battery of \bind{T}{42} tests on \bind{S}{17} subjects, for a total of \printExpectedValue expected values.
    However, since \bind{F}{210} values were defective, our successful measurement rate per subject was \printSucessfulRate.
\end{document}

MWE3

Still struggling with it. Any help is welcome (-;

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .