[Tex/LaTex] Calculating right angle triangle side inside LaTeX

calculations

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 ?

Best Answer

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