我正在寻找一种在 LaTeX 中进行最少浮点/整数计算的方法,目的不是为了编写包,而是为了生成实际文本。
下面是一个例子来说明。给定以下输入文本
The experiment included running a battery of \bind{T}{21} tests
on \bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
However, since \bind{F}{37} values were defective, our successful
measurement rate per subject was \use{100*round((V-F)/T,2)}\%
如果能转换成:
The experiment included running a battery of $21$ tests
on $13$ subjects, for a total of $273$ expected values.
However, since $37$ values were defective, our successful
measurement rate per subject was $86$\%.
其中\bind{C}{expression}
定义了一个新常量C
,其值为expression
,并返回C
,而\use{C}
仅返回 的值C
。有什么建议吗?
我知道这个spreadtab
包裹和这个问题:这个问题,但我还在寻找更多的东西。
答案1
如果您不介意使用 luatex,那么您可以让 lua 进行计算。我使用 ConTeXt,但我相信luacode
LaTeX 中的包提供了类似的功能。
\startusercode
round = global.math.round
\stopusercode
\def\bind#1#2%
{\usercode{#1 = #2}\use{#1}}
\def\use#1%
{\usercode{global.context(#1)}}
\starttext
The experiment included running a battery of \bind{T}{21.0} tests
on \bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
However, since \bind{F}{37} values were defective, our successful
measurement rate per subject was \use{100*round((V-F)/T,2)}\%
\stoptext
我使用usercode
而是luacode
为了使我的定义(T=21
等)不污染全局命名空间。
编辑:如果希望结果为数学模式,请在定义中更改global.context(...)
为global.context.math(...)
\use
答案2
另一个执行浮点计算的包是l3fp
。此包(目前)不允许用户定义新变量(但它支持,例如,因此原则上扩展它以支持用户定义变量应该不太难)。一种解决方法是遍历表达式并将所有变量、等pi
替换为内部变量、等,该变量保存相应的值。T
V
\l__my_T_fp
\l__my_V_fp
这是通过循环宏完成的\__my_use:N
,它一次读取一个字符。如果它是结束标记\q_recursion_tail
,那么我们就到达了表达式的末尾,因此跳到\q_recursion_stop
(完成\quark_if_recursion_tail_stop:n
)退出循环。如果变量\l__my_#1_fp
已定义,则使用它,否则将字符本身留给函数l3fp
接收。然后通过\__my_use:N
再次调用进行递归。
该\my_set:nn
函数确保\l__my_#1_fp
通过 定义\fp_zero_new:c
,然后设置其值。
\documentclass{article}
\usepackage{expl3, xparse}
\ExplSyntaxOn
\cs_new:Npn \__my_use:N #1
{
\quark_if_recursion_tail_stop:n {#1}
\cs_if_exist_use:cF { l__my_#1_fp } {#1}
\__my_use:N
}
\cs_new:Npn \my_use:n #1
{ \fp_eval:n { \__my_use:N #1 \q_recursion_tail \q_recursion_stop } }
\cs_new_protected:Npn \my_set:nn #1#2
{
\fp_zero_new:c { l__my_#1_fp }
\fp_set:cn { l__my_#1_fp } { \my_use:n {#2} }
}
\NewDocumentCommand {\bind} { m m } { \my_set:nn {#1} {#2} \my_use:n {#1} }
\NewDocumentCommand {\use} { m } { \my_use:n {#1} }
\ExplSyntaxOff
\begin{document}
The experiment included running a battery of \bind{T}{21} tests
on \bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
However, since \bind{F}{37} values were defective, our successful
measurement rate per subject was \use{100*round((V-F)/V,2)}\%
\end{document}
我还修复了最后一个公式,即(V-F)/T
。
答案3
我已经广泛使用fp
来完成这类工作。下面是一些函数的最小版本,可以将温度单位从一个系统转换为另一个系统。此站点上有很多使用该fp
包的示例。代码siunitx
也使用该包进行格式化。
例如,要将华氏度转换为摄氏度,请输入\Ftoc{32.999}
。这是一个简单的例子。
\documentclass{article}
\usepackage{fp}
\usepackage{siunitx}
\gdef\numdec{3}
\begin{document}
%\SetConversion{C}{K}{273.15}
\def\CtoK#1{\FPadd\result{#1}{273.15}%
\FPround\result{\result}{2}%
\sisetup{%
fixed-exponent = 2,
scientific-notation = false}
\num{\result}}
%% Convert Centigrate to Fahreneit
\def\CtoF#1{\FPdiv\resulta{9}{5}%
\FPmul\resultb{\resulta}{#1}%
\FPadd\resultc{\resultb}{32}%
\FPround\resultc{\resultc}{3}%
\num{\resultc}%
\sisetup{%
fixed-exponent = 0,
scientific-notation = false}
}
%% Convert Fahreneit to Centigrade
\def\FtoC#1{\FPdiv\resulta{5}{9}%
\FPsub\resultb{#1}{32}%
\FPmul\resultc{\resultb}{\resulta}%
\FPround\resultc{\resultc}{\numdec}%
\sisetup{%
fixed-exponent = 0,
scientific-notation = false}
\num{\resultc}%
}
%% Convert Fahreneit to Rankine
\def\FtoRa#1{\FPmul\result{#1}{9}%
\FPdiv\result{\result}{5}%
\FPround\result{\result}{\numdec}%
\result%
}
%% Convert Kelvin to Rankine
\def\KtoRa#1{\FPadd\result{#1}{459.67}%
\FPround\result{\result}{\numdec}\result}
%% Convert Rankine to Celcius
\def\RtoC#1{\FPsub\result{#1}{459.67}% to fahreneit
\FtoC{\result}}
\FtoC{32.999}
\end{document}
答案4
为了多样化,这里提供了一个基于 LuaLaTeX 的解决方案。它提供了两个 LaTeX 宏:\bind
和\use
。此外,由于 Lua 没有提供内置round
函数,因此它提供了 的实现round
,供在\use
指令中使用。
% !TEX TS-program = lualatex
\documentclass{article}
%% The LaTeX macros "\bind" and "\use" invoke "\directlua{...}"
%% to perform their task(s).
\newcommand\bind[2]{\directlua{#1=#2;tex.sprint(#1)}}
\newcommand\use[1]{\directlua{tex.sprint(#1)}}
%% Define two auxiliary functions: "round2int" and "round"
\directlua{%
function round2int ( x )
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end
function round ( num , digits )
return round2int ( num * 10^digits ) / 10^digits
end
}
\begin{document}
The experiment included running a battery of \bind{T}{21} tests on
\bind{S}{13} subjects, for a total of \bind{V}{T*S} expected values.
However, since \bind{F}{37} values were defective, our successful
measurement rate per subject was \use{100*round((V-F)/V,2)}\%. More
precisely, the successful measurement rate per subject was
\use{100*round((V-F)/V,4)}\%.
\end{document}