How to add, subtract, multiply, and divide in plain TeX?
You can do arithmetic (with +
, -
, *
, /
, but no ^
for powers) using \numexpr
expressions.
The \numexpr
expressions are among the e-TeX
extensions to the Knuth's TeX
.
(e-TeX extensions: on modern installations they are activated by default, except if you use the executable named tex
on the command line)
However you can't use truly fractional numbers inside \numexpr...\relax
(for example doing 1/7+1/3
). And /
does therein a (rounded) division to the nearest integer. Integers must be between -2147483647
and +2147483647
: this is the same limitation as for the integers one can store in a TeX \count
or in a LaTeX counter
; and a \count
can be used directly inside a \numexpr
expression, for a LaTeX counter
one has \value{mycountername}
.
I wrote package xintexpr which can be used either in Plain TeX (\input xintexpr.sty
) or in LaTeX (\usepackage{xintexpr}
).
if you want the final result rounded to an integer use
\xintiexpr
, for example\xintiexpr 123456789987654321/(2^32+3^20)\relax
. Else\xintexpr
will compute a fraction.if you want the fraction in irreducible form, use the
reduce
function:\xintexpr reduce (1/1+1/2+1/3+1/4+1/5+1/6+1/7+1/8+1/9+1/10)\relax
.contrarily to
\numexpr
which can be used directly in places where TeX expect a (whole) number, for example in an\ifnum
test, an\xintiexpr
needs to be prefixed by\xintthe
:\xintthe\xintiexpr
(or\xinttheiexpr
). But naturally, the produced number should be less than the2^31
limit.an
\xintthe\xintexpr
(or, shorter\xinttheexpr
) can not be used in an\ifnum
test, as TeX does not understand fractions. The package provides tests of its own to compare numbers incl. fractions.an
\xintexpr
expression must be terminated by a\relax
whereas a\numexpr
will terminate legally on any token (like a dot.
) not expected by its syntax.\numexpr -(1+2)\relax
does not work! but\xintexpr -(1+2)\relax
does...\xintexpr
is completely expandable.
Hence things such as:
\message{\xinttheexpr reduce (1/1+1/2+1/3+1/4+1/5+1/6+1/7+1/8+1/9+1/10)\relax}
\message {\xinttheiexpr 123456789987654321/(2^32+3^20)\relax}
are possible.
Code for the OP use case:
\input xintexpr.sty\relax % compile with etex or pdftex
% macro to use \xinttheiexpr which will round to the nearest whole number.
\def\roundandprint #1{\xinttheiexpr #1\relax }
\def\weeks{20} % term is twenty weeks
\def\lessonsperweek{3} % meets three times per week
\def\lessonduration{60} % meets for sixty minutes
\def\percentlectures{.5} % half the time is spent in lectures
\def\percentdiscussions{.2} % 20 percent of the time is spent in disucssions
% remaining percent of time left to lab work:
\def\percentlab{\xintexpr 1 - \percentdiscussions - \percentlectures\relax}
% minutes spent in lessons:
\def\totalduration{\xintexpr \lessonduration * \lessonsperweek * \weeks\relax}
% total duration in hours:
\def\totaldurationhours{\xintexpr \totalduration / 60 \relax}
% minutes spent in the lab:
\def\labduration{\xintexpr \totalduration * \percentlab \relax}
Total time in lab: \roundandprint{\labduration} minutes.
Total minutes in class: \roundandprint{\totalduration} minutes.
Total hours in class: \roundandprint{\totaldurationhours} hours.
\def\weeks{19} % these two values are changed later in the document
\def\lessonsperweek{2}
Total minutes in class: \roundandprint{\totalduration} minutes. % this value
% would be different because of the chagnes to
% \weeks and \lessonsperweek
\bye
Remark: the various \xintexpr ... \relax
in \percentlab
etc... are a bit optional; they could have been replaced by parentheses, but using such \xintexpr ... \relax
sub-expressions provides the maximal flexibility. For example, one can embed them in an \edef
, naturally if everything involved in the computation is defined at that time.
As mentioned in a prior example the ^
for powers is accepted: but the exponent must be an integer (3^(10/2)
is ok for \xintexpr
, which will correctly compute 3^5
). There is a sqrt
function, which computes the square-root with, by default, 16
digits of precision. A second optional argument allows more precision:
\xinttheexpr sqrt(2,60)\relax
gives 60
digits of precision. To get the result in scientific notation, there is:
\xintthefloatexpr sqrt(2,60)\relax
plain tex (uniquely) is usually used with the classic Tex engine (or at least with pdf and e-tex extensions disabled) so there is no infix arithmetic
\newcount\zzz
\zzz=5
\multiply\zzz by 3
\advance\zzz by 2
sets \zzz
to 17. If you use the plain format with e-tex you can use e-tex infix arithmentic
\newcount\zzz
\zzz=\numexpr 5*5 + 2\relax
You can use PGFmath.
For \pgfmathprintnumber
from (pgfmathfloat.code.tex
) it is necessary to define \pgf@texdist@protect
for reasons unknown to me (this seems to be some protection mechanism).
Simply \input
ting pgf.tex
works, too.
Code
\expandafter\def\csname pgf@texdist@protect\endcsname{}%
\input pgfmath.tex
\def\weeks{20} % term is twenty weeks
\def\lessonsperweek{3} % meets three times per week
\def\lessonduration{60} % meets for sixty minutes
\def\percentlectures{.5} % half the time is spent in lectures
\def\percentdiscussions{.2} % 20 percent of the time is spent in disucssions
% remaining percent of time left to lab work
\def\percentlab{1 - \percentdiscussions - \percentlectures}
% minutes spent in lessons
\def\totalduration{\lessonduration * \lessonsperweek * \weeks}
\def\totaldurationhours{(\totalduration) / 60}
\def\labduration{(\totalduration) * (\percentlab)} % minutes spent in the lab
\pgfset{
number format/.cd,
min exponent for 1000 sep=4,
int detect,
}
Total time in lab: \pgfmathprint{\labduration} (\pgfmathprintnumber[fixed,precision=0]{\pgfmathresult}) minutes.
Total minutes in class: \pgfmathprint{\totalduration}
(\pgfmathprintnumber{\pgfmathresult}) minutes.
Total hours in class: \pgfmathprint{\totaldurationhours}
(\pgfmathprintnumber{\pgfmathresult}) hours.
\def\weeks{19} % these two values are changed later in the document
\def\lessonsperweek{2}
% minutes spent in lessons
% this value would be different because of the chagnes to \weeks and \lessonsperweek
Total minutes in class: \pgfmathprint{\totalduration}
(\pgfmathprintnumber{\pgfmathresult}) minutes.
\bye