expl3 中有几种类型化的模式匹配命令,例如\tl_case
等\str_case
,包括\int_case
,但值得注意的是,似乎没有\fp_case
与匹配浮点值相对应的命令。我正在使用当前的l3kernel 文档以供参考。
我是否遗漏了一些琐碎的东西,或者\fp_case
目前确实遗漏了什么?如果是这样,我将非常感激一些关于如何实现它的指示,因为我对使用 LaTeX3 内部组件还不熟悉。TIA。
答案1
它不存在,因为它没有太多实际意义。特定计算中的任何不准确性都会导致您的代码执行不同的操作,即使3.1415926535897932
和3.1415926535897931
实际上没有区别。例如,这个:
\fp_compare:nNnTF { sin(2pi) } = { 0 }
{ \TRUE } { \FALSE }
结果为假,尽管从分析上来说它应该是正确的。
浮点相等通常是错误的操作。大多数情况下,浮点相等是可行的,因为浮点引擎非常擅长其工作,但如果你给它们一个棘手的情况,它就会得到错误的结果。事实上,当我以前编写 Fortran 代码时,gfortran
会引发有关浮点相等的编译时警告(我没有安装它来举例,抱歉)。
问题在于\fp_case:nn
它只使用相等性测试来选择一个案例,这可能会起作用,是的,但也可能出错,所以主要是为了稳定性,它不存在。
话虽如此,我们做有\bool_case_true:n(TF)
和\bool_case_false:n(TF)
,您可以使用它们来模拟\fp_case:nn
检查值是否在设定的容差范围内匹配的。这是一个概念验证(未实现真/假分支):
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\fp_new:N \l_arets_tol_fp
\fp_set:Nn \l_arets_tol_fp { 1e-6 }
\cs_new:Npn \arets_fp_case:nn #1
{
\exp_args:Nf \__arets_fp_case:nn
{ \fp_eval:n {#1} }
}
\cs_new:Npn \__arets_fp_case:nn #1#2
{
\tl_map_tokens:nn {#2}
{ \__arets_fp_case_split:nn {#1} }
}
\cs_new:Npn \__arets_fp_case_split:nn #1#2
{ \__arets_fp_case:nnn {#1} #2 }
\cs_new:Npn \__arets_fp_case:nnn #1#2#3
{
\fp_compare:nT
{
#1 > #2 - \l_arets_tol_fp &
#1 < #2 + \l_arets_tol_fp
}
{ \tl_map_break:n {#3} }
}
% ------------ Test macro
\cs_new_protected:Npn \test #1
{
$
\arets_fp_case:nn {#1}
{
{ { pi } { \pi } }
{ { exp(1) } { e } }
}
= \fp_eval:n {#1} $
}
\ExplSyntaxOff
\begin{document}
\test{3.1415926}
\test{2.7182818}
\test{1.6180339}
\end{document}
打印结果为:
答案2
您可以使用\str_case:nn
或,\str_case_e:nn
只要所有浮点数都以十进制表示即可。
\token_case_meaning:Nn
只要您只比较浮点变量,您也可以使用。
当然,测试浮点相等性的常见问题(正如 Phelype Oleinik 所指出的)仍然存在。
\documentclass{article}
\ExplSyntaxOn
\cs_new:Npn \__my_fp_case:nn #1#2
{ \str_case_e:en { \fp_to_decimal:n {#1} } {#2} }
\cs_new:Npn \__my_fp_case_test:n #1
{
\__my_fp_case:nn {#1}
{
{ \fp_to_decimal:N \c_one_fp } { <code1> }
{ 2 } { <code2> }
{ \fp_to_decimal:n { 0.6 * 2 } } { <code3> }
{ \fp_to_decimal:n { 1.2 ^ 2 } } { <code4> }
}
}
\NewExpandableDocumentCommand \fpcasetest { m }
{ \__my_fp_case_test:n {#1} }
\ExplSyntaxOff
\begin{document}
\fpcasetest{1}
\fpcasetest{2}
\fpcasetest{1.2}
\fpcasetest{1.44}
\bigskip
% just for demonstration; please don't use ExplSyntax at document level
\ExplSyntaxOn
\cs_new:Npn \__my_fp_case_token_meaning:N #1
{
\token_case_meaning:Nn #1
{
\c_zero_fp { <code1> }
\c_one_fp { <code2> }
\c_e_fp { <code3> }
\c_pi_fp { <code4> }
}
}
\__my_fp_case_token_meaning:N \c_zero_fp
\par
\fp_set:Nn \l_tmpa_fp {1}
\__my_fp_case_token_meaning:N \l_tmpa_fp
\par
\__my_fp_case_token_meaning:N \c_e_fp
\par
\fp_set_eq:NN \l_tmpa_fp \c_pi_fp
\__my_fp_case_token_meaning:N \l_tmpa_fp
\par
\ExplSyntaxOff
\end{document}