四象限浮点反正切函数

四象限浮点反正切函数

经过昨天的聊天讨论,我决定编写一个反正切函数,将角度标准化为 [0,2\pi] 范围内(当然,以度为单位,[0,360] 范围内)。这样做很容易。我的问题是,除了将其原生添加到 LaTeX3 中,有没有更好的方法可以做到这一点,以便可以对其进行评估\fp_eval:n,例如,

\fp_eval:n { atannorm(0.5,sqrt(3)/2)}

% !TEX program = lualatexmk
% !TEX encoding = UTF-8 Unicode

\documentclass{article}

\ExplSyntaxOn
\cs_new_protected:Nn \joe_atannormd:n {%
  % create and set a temporary list to #1
  \clist_set:Nn \l_tmpa_clist { #1 } 
  % need to extract the first argument
  \fp_set:Nn \l_tmpa_fp { \clist_item:Nn \l_tmpa_clist { 1 } }
  % normalize to [0,360] if the first argument < 0
  \fp_eval:n { \fp_compare_p:n { \l_tmpa_fp < 0 }  ? atand(#1) + 360 : atand(#1) } 
}%
\NewDocumentCommand{\atannormd}{ r() }{%
  \joe_atannormd:n { #1 }
}%

\cs_new_protected:Nn \joe_atannorm:n {%
  % create and set a temporary list to #1
  \clist_set:Nn \l_tmpa_clist { #1 } 
  % need to extract the first argument
  \fp_set:Nn \l_tmpa_fp { \clist_item:Nn \l_tmpa_clist { 1 } }
  % normalize to [0,2pi] if the first argument < 0
  \fp_eval:n { \fp_compare_p:n { \l_tmpa_fp < 0 }  ? atan(#1) + 2*pi : atan(#1) } 
}%
\NewDocumentCommand{\atannorm}{ r() }{%
  \joe_atannorm:n { #1 }
}%
\ExplSyntaxOff

\begin{document}

Normalized to \( 2\pi \):

\( \atannorm(0,1) \)

\( \atannorm(1,1) \)

\( \atannorm(1,0) \)

\( \atannorm(1,-1) \)

\( \atannorm(0,-1) \)

\( \atannorm(-1,-1) \)

\( \atannorm(-1,0) \)

Normalized to \( 360^\circ \):

\( \atannormd(0,1)^\circ \)

\( \atannormd(1,1)^\circ \)

\( \atannormd(1,0)^\circ \)

\( \atannormd(1,-1)^\circ \)

\( \atannormd(0,-1)^\circ \)

\( \atannormd(-1,-1)^\circ \)

\( \atannormd(-1,0)^\circ \)

\( \atannormd(0.5,sqrt(3)/2)^\circ \)

\end{document}

MWE 输出

答案1

您可以定义atannorm为“fp 字”,这样\fp_eval:n { atannorm(#1,#2) }可以工作,但这需要使用l3fp内部函数,所以这不是一个好主意。以在函数调用中多一个反斜杠为代价,您可以定义一个可扩展函数,\atannorm其必需参数由 分隔()

\NewExpandableDocumentCommand \atannorm { r() }
  { \joe_atannorm:nnn {#1} { atan } { 2*pi } }

(第二和第三个参数是为了您可以根据相同的内部定义\atannorm\atannormd)然后在逗号处拆分:

\cs_new:Npn \joe_atannorm:nnn #1
  { \__joe_atannorm:wwnn #1 \scan_stop: }

并将其传递给将所有内容组合在一起的宏:

\cs_new:Npn \__joe_atannorm:wwnn #1 , #2 \scan_stop: #3 #4
  { \use:e { ( #3 (#1,#2) \fp_compare:nNnT {#1} < { 0 } { + #4 } ) } }

代码不会检查输入是否确实包含,,如果不包含,则会抛出低级错误(如果需要,可以很容易地添加)。诀窍\use:e就在这里,因为l3fp逐步扩展可能会相当慢,所以可以\use:e加快速度。添加了一组额外的括号,这样就可以\atannorm(-1,-1)*2按预期工作。

完整代码如下:

\documentclass{article}
\usepackage{xfp}

\ExplSyntaxOn
\NewExpandableDocumentCommand \atannorm { r() }
  { \joe_atannorm:nnn {#1} { atan } { 2*pi } }
\NewExpandableDocumentCommand \atannormd { r() }
  { \joe_atannorm:nnn {#1} { atand } { 360 } }
\cs_new:Npn \joe_atannorm:nnn #1
  { \__joe_atannorm:wwnn #1 \scan_stop: }
\cs_new:Npn \__joe_atannorm:wwnn #1 , #2 \scan_stop: #3 #4
  { \use:e { ( #3 (#1,#2) \fp_compare:nNnT {#1} < { 0 } { + #4 } ) } }
\ExplSyntaxOff

\begin{document}

Normalized to \( 2\pi \):

\( \fpeval{ \atannorm (0,1) } \)

\( \fpeval{ \atannorm (1,1) } \)

\( \fpeval{ \atannorm (1,0) } \)

\( \fpeval{ \atannorm (1,-1) } \)

\( \fpeval{ \atannorm (0,-1) } \)

\( \fpeval{ \atannorm (-1,-1) } \)

\( \fpeval{ \atannorm (-1,0) } \)

Normalized to \( 360^\circ \):

\( \fpeval{ \atannormd (0,1) }^\circ \)

\( \fpeval{ \atannormd (1,1) }^\circ \)

\( \fpeval{ \atannormd (1,0) }^\circ \)

\( \fpeval{ \atannormd (1,-1) }^\circ \)

\( \fpeval{ \atannormd (0,-1) }^\circ \)

\( \fpeval{ \atannormd (-1,-1) }^\circ \)

\( \fpeval{ \atannormd (-1,0) }^\circ \)

\( \fpeval{ \atannormd (0.5,sqrt(3)/2) }^\circ \)

\end{document}

相关内容