15

I'd like compute the width of a screen from it's diagonal and its aspect ratio inside a LaTeX document. The formula is easy to get from the Pythagorean theorem, but needs square roots.

I currently compute this using the calc package to compute the square root with the Babylonian method, but it is not very elegant. What is the best way to compute square roots (and other functions) inside latex ?

7 Answers7

19

This is an interesting question. First we need to compare and analyse some answers

1) With Lua : This is the future and it's very accurate but unfortunately it's not enough used and a lot of TeX's users works only with pdf(la)TeX.

2) fp solution is fine but we need to use it with precaution because this can be slow. It's the method that I prefer. But a problem can arrive with fp if you need to get the result of (-1.5)^(2).

The next code is from Christian Tellechea 2009 modified by me.

\documentclass{scrartcl}
\usepackage{fp}   
\makeatletter  
\def\FP@pow#1#2#3{%
 \FP@beginmessage{POW}%
 {\def\FP@beginmessage##1{}%
 \def\FP@endmessage##1{}%
 \FPifzero{#2}%
     \expandafter\@firstoftwo
 \else
     \expandafter\@secondoftwo
 \fi
 {\FP@pow@zero{#3}}%
 {\FPifint{#3}%
     \expandafter\@firstoftwo
 \else
    \expandafter\@secondoftwo
 \fi  
{\FPifneg{#2}%
 \FPneg\FP@tmpd{#2}%
 \FPln\FP@tmpd\FP@tmpd
 \FPmul\FP@tmpd\FP@tmpd{#3}%
 \FPexp\FP@tmpd\FP@tmpd
 \FPtrunc\FP@tmp{#3}0%
 \ifodd\FP@tmp
     \FPneg\FP@tmp\FP@tmpd
 \else
     \let\FP@tmp\FP@tmpd
 \fi
\else
 \FPln\FP@tmpd{#2}%
 \FPmul\FP@tmpd\FP@tmpd{#3}%
 \FPexp\FP@tmp\FP@tmpd
\fi
}% 
{\FPln\FP@tmpd{#2}%
 \FPmul\FP@tmpd\FP@tmpd{#3}%
 \FPexp\FP@tmp\FP@tmpd}%
}%
\global\let\FP@tmp\FP@tmp}%
\FP@endmessage{}%
\let#1\FP@tmp}    
\makeatletter
\begin{document}

\FPpow\temp{-1.5}{2}
\temp 

\FPpow\temp{-2}{3}
\temp 
\end{document}  

3) pgfmath . The Martin's answer describes the traditional method. It can also be slow and sometimes inaccurate for extreme values. The next picture is to illustrate inaccurate results sometimes with pgfmath. The picture comes from the pgfmanual in the paragraph The Syntax of Projection Modifiers. The intersections of the three altitudes are fine with small sizes but with a big zoom we get this :

enter image description here

4) with fpu. It's possible to use fpu It's a package inside pgf.

\documentclass{article} 
\usepackage{tikz}
\usepackage{fp}   
\usetikzlibrary{fpu}
\newlength{\lengtha}
\newlength{\lengthb}  
\begin{document} 

\pgfkeys{/pgf/fpu,/pgf/fpu/output format=fixed} 
\setlength{\lengtha}{1pt}
\setlength{\lengthb}{1pt}
\pgfmathparse{sqrt(\lengtha*\lengtha + \lengthb*\lengthb)}
\pgfmathresult 
\end{document}

The result is 1.414192000000000. This strange because pgfmath in this case gives 1.41421 while fp with the same values gives 1.414213562373095042.

5) \usetikzlibrary{fixedpointarithmetic} Another possibility is the fixed point arithmetic. It's again inside pgf

\documentclass{article} 
\usepackage{tikz}
\usepackage{fp}   
\usetikzlibrary{fixedpointarithmetic} 
\newlength{\lengtha}
\newlength{\lengthb}  
\begin{document}      
\setlength{\lengtha}{1pt}
\setlength{\lengthb}{1pt}
\pgfkeys{/pgf/fixed point arithmetic={scale results=10^-6}} 
\pgfmathparse{sqrt(\lengtha*\lengtha + \lengthb*\lengthb)}
\pgfmathresult  
\end{document}

The result is as with fp, it's normal fp is used !! result = 1.414213562373095042

6) \usepackage{tkz-euclide} When you need to calculate the distance between two nodes, you can use a macro that I define in my package: tkz-euclide. It's a mix with fp.

\begin{tikzpicture}
\coordinate (A) at (3,0);
\coordinate (B) at (0,4);

 \tkzCalcLength[cm](A,B) 

 \node {\tkzLengthResult } ;
\draw (A) circle (\tkzLengthResult cm) ;
\draw[fill=red] (A) circle (2pt) ; 
\draw[fill=red] (B) circle (2pt) ;  
\end{tikzpicture}

\tkzLengthResult gives 5.00000 There is an option to get the result in pt or in cm.

7) Finally the last solution used TeX. This solution has been written by a friend J_C Charpentier. This is interesting. The macro is named \pythagore and this macro uses another macro \SQRTto get the square root of an integer < 1962446671 .

\documentclass{article}    
\makeatletter
\newcount\@tempcntc
\newcommand\SQRT[2]{%
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % #2 = \sqrt{#1} (valeur entière)    %
  % #2 est une longueur                %
  % #1 ne doit pas dépasser 1962446671 %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % Initialisations
  \@tempcnta #1\relax % Sauvegarde valeur
  \@tempcntb 1 % juste pour le premier test
  \@tempcntc #1\relax % Terme en cours
  % simili racine pour le premier terme !!!!
  \ifnum \@tempcntc > 1073741824 % 2^{30}
    \divide \@tempcntc 32738
  \else\ifnum \@tempcntc > 268435456 % 2^{28}
    \divide \@tempcntc 16384
  \else\ifnum \@tempcntc > 67108864 % 2^{26}
    \divide \@tempcntc 8192
  \else\ifnum \@tempcntc > 16777216 % 2^{24}
    \divide \@tempcntc 4096
  \else\ifnum \@tempcntc > 4194304 % 2^{22}
    \divide \@tempcntc 2048
  \else\ifnum \@tempcntc > 1048576 % 2^{20}
    \divide \@tempcntc 1024
  \else\ifnum \@tempcntc > 262144 % 2^{18}
    \divide \@tempcntc 512
  \else\ifnum \@tempcntc > 65536 % 2^{16}
    \divide \@tempcntc 256
  \else\ifnum \@tempcntc > 16384 % 2^{14}
    \divide \@tempcntc 128
  \else\ifnum \@tempcntc > 4096 % 2^{12}
    \divide \@tempcntc 64
  \else\ifnum \@tempcntc > 1024 % 2^{10}
    \divide \@tempcntc 32
  \else\ifnum \@tempcntc > 256 % 2^{8}
    \divide \@tempcntc 16
  \else\ifnum \@tempcntc > 64 % 2^{6}
    \divide \@tempcntc 8
  \else\ifnum \@tempcntc > 16 % 2^{4}
    \divide \@tempcntc 4
  \else\ifnum \@tempcntc > 4 % 2^{2}
    \divide \@tempcntc 2
  \else\ifnum \@tempcntc > 2 % 2^{1}
    % c'est 3 ou 4, l'arrondi est toujours 2
    \@tempcntc 2
    \@tempcntb 0 % pas de boucle !
  \else\ifnum \@tempcntc > 1
    % C'est 2, l'arrondi est 1
    \@tempcntc 1
    \@tempcntb 0 % pas de boucle !
  \else
    % c'est 0 ou 1 la racine est le nombre
    \@tempcntb 0 % pas de boucle !
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi % joli !
  % Boucle principale (Newton)
  \loop
  \ifnum\@tempcntb>0
    % Calcul du terme suivant
    \@tempcntb \@tempcnta
    \divide \@tempcntb \@tempcntc
    \advance \@tempcntc \@tempcntb
    \divide \@tempcntc 2
    % On regarde où on en est
    % (différence entre l'itération au carré et le nombre initial)
    \@tempcntb \@tempcntc
    \multiply \@tempcntb \@tempcntb
    \advance \@tempcntb -\@tempcnta
  \repeat
  % Sauvegarde du résultat
  #2\the\@tempcntc sp\relax
}
\newcommand*\pythagore[3]{%
  \dimen0=#1\relax
  \@tempcnta \dimen0
  \dimen0=#2\relax
  \@tempcntb \dimen0
  % Pour éviter les débordements
  % on travaille en 1/32 de point
  \divide \@tempcnta 2048
  \divide \@tempcntb 2048
  % calcul de a*a+b*b
  \multiply \@tempcnta \@tempcnta
  \multiply \@tempcntb \@tempcntb
  \advance \@tempcnta \@tempcntb
  % appel de la racine
  \SQRT{\the\@tempcnta}{#3}%
  % Fin des débordements !
  #3 2048#3\relax
}
\makeatother

\newlength{\result}        
\begin{document} 

\pythagore{2pt}{2pt}{\result}
\the\result 

\pythagore{3pt}{4pt}{\result} 
\the\result

\pythagore{6pt}{8pt}{\result} 
\the\result 
\end{document}     

Results are : 2.8125pt ; 5.0 pt and 10.0pt

Torbjørn T.
  • 206,688
Alain Matthes
  • 95,075
14

I would use fp's power function for square roots and the like. With fp's accuracy you will also minimize round off errors to near 15 decimal places.

Consider the square root of 10. You write it as a power function:

\FPpow\temp{10}{0.5}

We get 3.162277660168379312

You access the value in the macro \temp (it can be any name, fp will define it on the fly).

Translating this back to a power of two we get:

 \FPpow\temp{\temp}{2}
 \FPround\temp{\temp}{15}
 \temp

 10.000000000000000

Minimal shown below:

\documentclass{article}
\usepackage{fp}
\begin{document}
\FPpow\temp{10}{0.5}
\temp

\FPpow\temp{\temp}{2}
\FPround\temp{\temp}{15}
\temp
\end{document}

The \temp variable used accumulates the results. No need to clutter the namespace.

yannisl
  • 117,160
  • @Yannis : The problem with fp and \FPpow arrives with \FPpow\temp{-2}{2}. With lengths (positive values) is not a problem but the problem exists ! I modified my answer to give a solution for this problem. – Alain Matthes Apr 05 '11 at 21:07
  • @Altermundus If one expects negative values in the input then upstream checks are essential eg, \FPpow\temp{-1}{0.5}! – yannisl Apr 06 '11 at 02:02
  • yes you are right with .5 but if the exponent is an integer it's damage to get an error ( with a polynomial function for example) – Alain Matthes Apr 06 '11 at 04:40
  • I don't see how that can be considered a continuation. If it needs to be analogized, it's acting like an accumulator. – Jay Kominek Jun 27 '11 at 22:16
8

You can use the math capabilities of the vector graphics package PGF:

\documentclass{article}
\usepackage{pgf}
\pgfmathsetmacro{\diagonal}{1280}
\pgfmathsetmacro{\aspectratio}{4/3}
\pgfmathsetmacro{\screenwidth}{(\diagonal*\aspectratio)/sqrt(\aspectratio^2+1)}
\begin{document}
    \screenwidth
\end{document}

Unfortunately, the result is 1024.0023, so it is not absolutely accurate. If you need only the integer part of the result, you can use \pgfmathtruncatemacro{\screenwidth}{(\diagonal*\aspectratio)/sqrt(\aspectratio^2+1)} instead, which cuts off the decimal places.

diabonas
  • 25,784
8

If you are willing to compile with lualatex, you can let Lua do the calculations:

\documentclass{article}
\begin{document}

\def\diagonal{1280}
\def\aspectratio{4/3}
\edef\screenwidth{\directlua{tex.print (\diagonal*\aspectratio/math.sqrt((\aspectratio)^2+1))}}

\show\screenwidth
\end{document}

gives

> \screenwidth=macro:
->1024.

Calculations performed by Lua are more precise then calculations with TeX lengths (i.e. what calc and pgf use).

Caramdir
  • 89,023
  • 26
  • 255
  • 291
  • 1
    true about accuracy with calc and pgf. The fp package will, however, give you better accuracy than Lua. – yannisl Apr 11 '11 at 14:09
5

You can use pgfmath (pgf package) which gives you a sqrt function. However, like Harlad Hanche-Olsen already mentioned in his comment, it depends on the required accurate. This uses the precision of TeX length registers which has its limitations.

An example would be:

\documentclass{article}

\usepackage{pgf}

\newlength{\lengtha}
\newlength{\lengthb}

\begin{document}

\setlength{\lengtha}{3pt}
\setlength{\lengthb}{5pt}

\pgfmathparse{sqrt(\lengtha*\lengtha + \lengthb*\lengthb)}
\pgfmathresult

\end{document}
Martin Scharrer
  • 262,582
5

One can use the fp module of LaTeX3:

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\widthfromdiagonal}{mmmm}
  {
    \dim_set:Nn #4
      {
        \fp_to_dim:n
          { \dim_to_fp:n {#1} * (1 + ((#3) / (#2))^2)^-0.5 }
      }
  }
\ExplSyntaxOff

\widthfromdiagonal{13cm}{4}{3}{\textwidth}

The first argument is the length of the diagonal, the second and the third the aspect ratio, the fourth the dimension to set.

This will set the \textwidth parameter to 295.90866pt. Doing backwards the calculation with bc, this corresponds to a 12.9999999cm diagonal.

egreg
  • 1,121,712
3

You can use the xintexpr package for expressions or its sub-component xint and xintfrac for using macros. These macros are expandable. Hence they can be used directly in length definitions or assignments to counters, etc..., naturally if the number does not exceed the TeX bounds. The macros by themselves deal with arbitrarily big integers or fractions or decimal numbers.

Update because my initial answer had a very strange way of computing a diagonal!!! I make a poor Pythagorian...

\documentclass [a4paper]{article}

\usepackage{xint}

\usepackage{xintexpr}

\newlength\diagonal

\begin{document}

\setlength\diagonal {\xintiiSqrtR{\xintiiAdd{\xintiiSqr{\number\pdfpagewidth}}{\xintiiSqr{\number\pdfpageheight}}}sp}

From the length register defined via macros of \verb|xint| we get \the\diagonal.

We could have also used an expression inside the length definition, or
directly here without using a length we obtain directly \the\dimexpr
\xinttheiiexpr sqrtr(\pdfpagewidth^2+\pdfpageheight^2)\relax sp. This was
naturally rounded to integral number of scaled points, and used \TeX's own
conversion to points via \verb|\the|.

If we prefer to get the value in centimeters, here we go:
\xinttheiexpr [6] sqrt(\pdfpagewidth^2+\pdfpageheight^2)/65536/72.27*2.54\relax cm.

For people who want to check with their calculator, this is supposed to
correspond to the diagonal length of a rectangle with side lengths of
\xinttheiexpr [6]\pdfpagewidth/65536/72.27*2.54\relax cm and 
\xinttheiexpr [6]\pdfpageheight/65536/72.27*2.54\relax cm.
\end{document}

diagonal

  • People needing repetitive conversions from a length register to its value in centimeters with 1/1000cm of precision may use: \xintNewIExpr \ToCms [1]{[4] 1/65536/72.27*2.54*protect(\number #1)}. Then \ToCms {\mylength} or \ToCms\mylength does the conversion. Curious people will due \meaning\ToCms to see what it does (it does extra things not needed here but due to the fact that xintexpr allows comma separated things in general). Requires \input xintexpr.sty or \usepackage{xintexpr}. Or \def\ToCms #1{\xintRound {4}{\xintMul {127/236814336[0]}{\number #1}}} with only xintfrac –  Dec 01 '14 at 16:22