Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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 ?

share|improve this question
    
What is your desired accuracy? –  Harald Hanche-Olsen Apr 5 '11 at 14:42
    
For my application (display) a 1% is enough. –  Frédéric Grosshans Apr 5 '11 at 14:53
    
édéric 1% is generally ok; the only problem is : what is the amplitude of the lengths ? 1pt .. 5m The next problem is : Do you need a fast code ? –  Alain Matthes Apr 5 '11 at 20:50
    
@Altermundus : My current application is to convert a document for a phone screen, so the diagonal length would be around 15 cm. So the precision and speed are not crucial. But I can easily imagine situations where the size is bigger (in the order of 1 m, for scale 1:1 representation of a screen capture on a poster) and wher th need for speed would be more important (batch conversion of arXiv documents). And, of course, I feel that using a sub-optimal solution with TeX would be an oxymoron. –  Frédéric Grosshans Apr 6 '11 at 9:02

6 Answers 6

up vote 16 down vote accepted

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

share|improve this answer
    
excellent answer! –  Yiannis Lazarides Apr 6 '11 at 2:06

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.

share|improve this answer
    
@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 5 '11 at 21:07
    
@Altermundus If one expects negative values in the input then upstream checks are essential eg, \FPpow\temp{-1}{0.5}! –  Yiannis Lazarides Apr 6 '11 at 2: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 6 '11 at 4: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

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.

share|improve this answer

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).

share|improve this answer
1  
true about accuracy with calc and pgf. The fp package will, however, give you better accuracy than Lua. –  Yiannis Lazarides Apr 11 '11 at 14:09

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}
share|improve this answer

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.

share|improve this answer
    
Can you update your answer using the new fp? –  Bruno Le Floch Jun 25 '12 at 15:37
    
I don't have the new fp and clairvoyance is not among my skills. :) Please, if you decide to modify the answer, keep also the old code. –  egreg Jun 25 '12 at 15:53
    
sorry, I forgot that it isn't on CTAN yet. –  Bruno Le Floch Jun 25 '12 at 17:05
    
@BrunoLeFloch Will the old syntax cease to work? –  egreg Jun 25 '12 at 17:09
    
long-term, that's the plan. I don't quite remember the deprecation date, but I think it should be somewhere around the end of the year. –  Bruno Le Floch Jun 26 '12 at 5:27

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.