6

I'm working on a diagram and I'm pretty new to LaTeX, so I've decided to try to do it using math instead of statically assigned values. I have come up with the following:

\newcommand\qoffset{14}
\newcommand\smallx{8}
\newcommand\smally{4}
\newcommand\py{\the\numexpr\smally * (\qoffset / (\qoffset - \smallx))}
\the\numexpr\qoffset / (\qoffset - \smallx)

The answer should be 9.3 for \py but the division is rounding (or truncating) and I need decimal point precision. How would I go about doing this?

NOTE: I am not dead set on using \the\numexpr, it's just what I found when trying to figure out how to do math.

8

\numexpr works only on integers. If you want decimals you have to use \dimexpr (note that you have to add the pt unit to the numerator):

\documentclass{article}
\makeatletter
\def\dimeval#1{\strip@pt\dimexpr#1\relax}
\makeatother
\begin{document}
\newcommand\qoffset{14}
\newcommand\smallx{8}
\newcommand\smally{4}
\dimeval{\qoffset pt / (\qoffset - \smallx)}

\dimeval{\smally pt * (\qoffset / (\qoffset - \smallx))}
\end{document}

which results:

enter image description here

which is correct for the first one, but not as much for the second (it should be 9.33333).

The second one is wrong (depending on your point of view) because \dimexpr only does integer division and multiplication, so (replacing the values) 4pt * ( 14 / ( 14 - 8 ) ) evaluates to 4pt * ( 14 / 6 ) then to 4pt * 2.33333, and 2.33333 truncates to 2 and the result is 8. You can get a bit closer with

\dimeval{\smally pt * \dimeval{\qoffset pt / (\qoffset - \smallx)}}

but a bit wrong is still wrong.


If you want real floating point arithmetics (without having to worry about units), use \fpeval from the xfp package:

\documentclass{article}
\usepackage{xfp}
\begin{document}
\newcommand\qoffset{14}
\newcommand\smallx{8}
\newcommand\smally{4}
\fpeval{\qoffset / (\qoffset - \smallx)}

\fpeval{\smally * (\qoffset / (\qoffset - \smallx))}
\end{document}

which results

enter image description here


As Mico says, you might want rounding. If want for typesetting numbers the best option is the siunitx package:

\documentclass{article}
\usepackage{xfp}
\usepackage{siunitx}
\begin{document}
\num[round-mode=places, round-precision=5]%
  {\fpeval{4 * (14 / (14 - 8))}}
\end{document}

enter image description here

but if you want to round for further calculations, then you can use the round function directly (the syntax is round(<num>,<digits>)):

\documentclass{article}
\usepackage{xfp}
\begin{document}
\fpeval{round(  4 * (14 / (14 - 8))  ,5)}
\end{document}

enter image description here

| improve this answer | |
  • 1
    +1. :-) One could add that if the OP wishes to display the number rounded to, say, 3 digits, he/she could do so by (a) loading the siunitx package and (b) typing \num[round-mode=places, round-precision=3]{\fpeval{\qoffset / (\qoffset - \smallx)}}, i.e., by encasing the result of the calculation in a \num directive. – Mico Aug 11 at 4:05
  • \dimexpr is really weird. It will not do what you expect for a / b but it will do for c * a / b. So I usually make a scratch \dimen, write 1pt to it and use the second form to get proper floating point division. – Henri Menke Aug 11 at 20:28
4

Would you consider using LuaTeX? Then you could evaluate even more complicated expressions:

% gobble ".0" for integers and rounding function
\begingroup
\catcode`\%=12
\directlua{
   function math.round_int ( x )
     return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
   end
   function math.round ( x , n )
     return math.round_int ( x*10^n ) / 10^n 
   end
   function gobblezero(x)
     local y = math.round ( x , 8 )
     if y == math.floor(y) then
       return string.format ( "%.0f", y )
     else
       return math.round(y, 7)
     end
   end}
\endgroup

\def\fpeval#1{\directlua{tex.print(gobblezero(#1))}}

$$ \root 6 \of {64} = \fpeval{64^(1/6)} $$

With rounding:
%
$$ 1/3 = \fpeval{math.round(1/3, 1)}... $$

\bye

enter image description here

| improve this answer | |
  • 1
    Nice example! Though I have to say that xfp is a mostly IEEE-754 compliant floating point engine, so \fpeval{64^(1/6)} and \fpeval{round(1/3, 1)} produce the same output as Lua (and it's engine-independent :-) – Phelype Oleinik Aug 11 at 13:41
  • You are right of course, but if someone uses LuaTeX he can avoid many lines of code by using the floating point engine from Lua. ;) This should also be faster, I think. – Weißer Kater Aug 11 at 15:45
  • Indeed, using a built-in floating point engine is a lot faster than the macro emulation. – Phelype Oleinik Aug 11 at 16:44
  • Writing such macros is extremly difficult. I wanted to try this as I like programming in TeX, but I gave up soon. See: tex.stackexchange.com/questions/540758/… – Weißer Kater Aug 11 at 17:33
  • The implementation is indeed extremely complex. The xfp package I mentioned uses the expandable floating point engine from expl3. Take a look at the l3fp implementation (section 24, around page 700) if you're interested (I tried to understand the implementation, but have failed so far :-) – Phelype Oleinik Aug 11 at 17:44

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.