PGF math engine imprecise?
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}
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
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}