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.

The command \pgfmathparse{asin(1/\x)} yields for each \x in the range of 125 to 142 the same value, whereas \pgfmathparse{1/\x} yields the much more precise values for each \x. Is the ArcSin function of PGF that imprecise? What can i do to get more precise results?

MWE:

\documentclass[11pt]{minimal}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
    \foreach \x in {120,121,...,150}
    {
        \pgfmathparse{1/\x}\let\fraction\pgfmathresult
        \pgfmathparse{asin(\fraction)}\let\angle\pgfmathresult
        \node at (0, \x/2-60) {\x, \fraction, \angle}; 
    }
\end{tikzpicture}
\end{document}
share|improve this question
    
Related : tex.stackexchange.com/questions/76209/… –  percusse Oct 14 '12 at 12:46

3 Answers 3

up vote 16 down vote accepted

There are different math engines that can be used with pgf/TikZ.

Library fixedpointarithmetic

\documentclass[11pt]{minimal}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{fp}
\usetikzlibrary{fixedpointarithmetic}
\begin{document}
\begin{tikzpicture}[fixed point arithmetic]
    \foreach \x in {120,121,...,150}
    {
        \pgfmathparse{1/\x}\let\fraction\pgfmathresult
        \pgfmathparse{asin(\fraction)}\let\angle\pgfmathresult
        \node at (0, \x/2-60) {\x, \fraction, \angle};
    }
\end{tikzpicture}
\end{document}

fixedpointarithmetic

share|improve this answer

Yes it's imprecise (unless you use the fpu library) and also the engine doesn't compute value-by-value but instead uses a look-up table. But besides that TeX math capabilities are limited anyway. So you can also fake an approximation for yourself.

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
    \foreach \x[count=\xi] in {120,...,150}
    {
        \pgfmathsetmacro\myfraction{1/\x}
        \pgfmathsetmacro\myangle{asin(\myfraction)}
        \pgfmathsetmacro\modifiedangle{deg(\myfraction + 1/6*(\myfraction)^3 + 3/40*(\myfraction)^5)}
        \node[text width=3cm,align=left] (a-\xi) at (0,0.4*\xi) {\texttt{\myangle,\modifiedangle}};
    }
\end{tikzpicture}
\end{document}

asin vs. approximated asin

enter image description here

share|improve this answer
    
And why the downvote? –  percusse Oct 14 '12 at 13:54
    
No reason for downvote, a good answer, so +1 to compensate. –  Alexander Oct 14 '12 at 14:04
    
@percusse +1. Can you compare your approximation with the latest CVS version of pgf? –  Paul Gaborit Oct 14 '12 at 15:00
    
@PaulGaborit This numerical approx. holds in the domain about [-0.85,0.85] then the asin curve takes off vertically so probably taylor expansion is not the way to go. Within the domain, CVS version and this is comparable. So one might implement a hybrid scheme for the interpolation and the look-up table. –  percusse Oct 14 '12 at 16:52
    
@PaulGaborit Or we should make a low-order polynomial approximation (optimization) since Stone-Weierstrass is on our side ;) –  percusse Oct 14 '12 at 17:38

I recently found out that arctangent seems to be well approximated by a continued fraction on the interval [-1,1] (the rest of the real line reduces to that using arctan(1/x)=pi/2-arctan(x)). Here is a quick and dirty implementation of arctangent using l3fp. It seems to have at least 15 digits of accuracy, but there may be bugs.

\documentclass{article}
\usepackage{expl3, xparse}
\ExplSyntaxOn

%%%%% Defining the function atan2, such that atan2(y,x) is the angle of x+iy.
\int_const:Nn \c__atan_steps_int { 30 } % must be even
\fp_new:N \l__atan_x_fp
\fp_new:N \l__atan_y_fp
\fp_new:N \l__atan_offset_fp
\fp_new:N \l__atan_tmp_fp
\fp_new:N \l__atan_result_fp
\cs_new_protected:Npn \atan:nnN #1#2#3
  {
    \fp_set:Nn \l__atan_y_fp {#1}
    \fp_set:Nn \l__atan_x_fp {#2}
    \fp_zero:N \l__atan_offset_fp
    \fp_compare:nT { \l__atan_y_fp < - \l__atan_x_fp }
      {
        \fp_set:Nn \l__atan_x_fp { - \l__atan_x_fp }
        \fp_set:Nn \l__atan_y_fp { - \l__atan_y_fp }
        \fp_set_eq:NN \l__atan_offset_fp \c_pi_fp
      }
    \fp_compare:nT { \l__atan_y_fp > \l__atan_x_fp }
      {
        \fp_set_eq:NN \l__atan_tmp_fp \l__atan_x_fp
        \fp_set_eq:NN \l__atan_x_fp \l__atan_y_fp
        \fp_set:Nn \l__atan_y_fp { - \l__atan_tmp_fp }
        \fp_add:Nn \l__atan_offset_fp { pi / 2 }
      }
    % Now X >= 0 and -X <= Y <= X.
    \fp_compare:nTF { \l__atan_y_fp < 0 }
      {
        \fp_set:Nn \l__atan_y_fp { - \l__atan_y_fp }
        \__atan_compute:
        \fp_set:Nn #3 { \l__atan_offset_fp - \l__atan_result_fp }
      }
      {
        \__atan_compute:
        \fp_set:Nn #3 { \l__atan_offset_fp + \l__atan_result_fp }
      }
  }
\cs_new_protected_nopar:Npn \__atan_compute:
  {
    % atan(y/x)
    % = (y/x)(1-y^2/(3x^2+9y^2/(5+4y^2/(7x^2+25y^2/(9+16y^2/(11x^2+...))))))
    %
    \fp_set:Nn \l__atan_x_square_fp { \l__atan_x_fp * \l__atan_x_fp }
    \fp_set:Nn \l__atan_y_square_fp { \l__atan_y_fp * \l__atan_y_fp }
    \fp_set:Nn \l__atan_result_fp
      { ( 2 * \c__atan_steps_int + 3 ) * \l__atan_x_square_fp }
    \int_step_inline:nnnn \c__atan_steps_int { -2 } { 2 }
      {
        \fp_set:Nn \l__atan_result_fp
          {
            (2 * ##1 - 1) * \l__atan_x_square_fp
            + (##1 + 1)**2 * \l__atan_y_square_fp
              / ( 2 * ##1 + 1
                  + ##1 * ##1 * \l__atan_y_square_fp / \l__atan_result_fp )
          }
      }
    \fp_set:Nn \l__atan_result_fp
      {
        \l__atan_y_fp / \l__atan_x_fp
        * ( 1 - \l__atan_y_square_fp / \l__atan_result_fp )
      }
  }
%%%%% End of atan2.
\fp_new:N \l__asin_x_fp
\cs_new_protected:Npn \asin:nN #1#2
  {
    \fp_set:Nn \l__asin_x_fp {#1}
    \atan:nnN
      { \l__asin_x_fp }
      { 1 + (1 - \l__asin_x_fp ** 2) ** .5 }
      \l__asin_x_fp
    \fp_set:Nn #2 { 2 * \l__asin_x_fp }
  }
%%%%%
\fp_new:N \X
\NewDocumentCommand{\showasin}{m}
  {
    \fp_gset:Nn \X {#1}
    \fp_to_tl:N \X
    &
    \asin:nN { \X } \X
    \fp_to_tl:N \X
    \\
  }
\ExplSyntaxOff
\begin{document}

\begin{tabular}{cc}
  \showasin { 1 }
  \showasin { 1 - 1e-16 }
  \showasin { 1 - 1e-12 }
  \showasin { 1 - 1e-8 }
  \showasin { 1 - 1e-4 }
  \showasin { .9 }
  \showasin { .1 }
  \showasin { 1e-4 }
  \showasin { 1e-8 }
  \showasin { 1e-12 }
  \showasin { 1e-20 }
  \showasin { 1e-40 }
  \showasin { -1e-40 }
  \showasin { -.123 }
  \showasin { -1 }
\end{tabular}

\end{document}
share|improve this answer

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.