纯 TeX 中的比例运算

纯 TeX 中的比例运算

我想计算两个维度之间的比率并得出无量纲数量,以便在过程中进行进一步计算。我似乎无法做到这一点。具体来说,我遇到了两个奇怪的问题:首先,当我进行任何涉及的计算时\strip@pt,它都会打印数字。此外,计算出错误的比例,我不确定为什么。

\catcode`@=11

\begingroup
  \catcode`P=12
  \catcode`T=12
  \lowercase{
    \def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}}
  \expandafter\endgroup\x
\def\strip@pt{\expandafter\rem@pt\the}


\def\setdimenzerotofontheightanddepth#1#2{
\dimen0=\fontcharht\font`#1
\advance\dimen0 by \fontchardp\font`#1
\ifx#2
\else
  \advance\dimen0 by \fontcharht\font`#2
  \advance\dimen0 by \fontchardp\font`#2
\fi
}

\setdimenzerotofontheightanddepth fg
\edef\dimzeroamt{\strip@pt\dimen0}

Dimen0 is: \the\dimen0, Baselineskip is: \the\baselineskip

\dimen1=\baselineskip
Unfortunately it also shows the dimzeroamount when I do the calculation: \divide\dimen1 by \dimzeroamt

The ratio is: \strip@pt\dimen1

The real (manually calculated ratio) is $0.90947\dots$
\catcode`@=12

\bye

答案1

图形包中有划分尺寸的代码:

在此处输入图片描述

\documentclass{article}

\usepackage{graphics}

\makeatletter

\begin{document}


\def\setdimenzerotofontheightanddepth#1#2{
\dimen8=\fontcharht\font`#1
\advance\dimen8 by \fontchardp\font`#1
\ifx#2
\else
  \advance\dimen8 by \fontcharht\font`#2
  \advance\dimen8 by \fontchardp\font`#2
\fi
}

\setdimenzerotofontheightanddepth fg

Dimen8 is: \the\dimen8, Baselineskip is: \the\baselineskip


\Gscale@div\tmp \baselineskip{\dimen8}

gives \tmp

The real (manually calculated ratio) is $0.90947\dots$



\end{document}

答案2

这是一个完全纯文本解决方案。它改编自graphics包。

\catcode`@=11

\begingroup
  \catcode`P=12
  \catcode`T=12
  \lowercase{
    \def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}}
  \expandafter\endgroup\x
\def\strip@pt{\expandafter\rem@pt\the}


\def\setdimenzerotofontheightanddepth#1#2{
\dimen8=\fontcharht\font`#1
\advance\dimen8 by \fontchardp\font`#1
\ifx#2
\else
  \advance\dimen8 by \fontcharht\font`#2
  \advance\dimen8 by \fontchardp\font`#2
\fi
}

\setdimenzerotofontheightanddepth fg%
\edef\dimzeroamt{\strip@pt\dimen8 %
}%

\dimen0=\baselineskip%
\count0=65536
  \loop%
  \ifdim\dimen0<8192\p@%
    \dimen0=2\dimen0%
    \divide\count0 by 2 %
  \repeat
\divide\dimen8\count0
\divide\dimen0\dimen8
\strip@pt\dimen0
%\edef\x{\strip@pt\dimen0}

The real (manually calculated ratio) is $0.90947\dots$%
\catcode`@=12

\bye

答案3

TeX 的语法规则告诉你,你可以

\divide\dimen0 by <number>

其中<number>是整数。 在您的例子中, 的展开式\dimzeroamt13.19443,因此 TeX 会将其除以\dimen013 并打印.19443

您可以使用以下方法完成此操作expl3(也可以使用 Plain TeX):

\input expl3-generic

\ExplSyntaxOn
\cs_new_protected:Nn \ioiooiioio_getfactor:Nnnn
 {% #1 is a control sequence, #2 the dimension, #3 and #4 two characters
  \tl_set:Nx #1
   {
    \fp_eval:n
     {
      \dim_to_fp:n { #2 } /
      \dim_to_fp:n
       {
        \fontcharht\font`#3 + \fontcharht\font`#4
        +
        \fontchardp\font`#3 + \fontchardp\font`#4
       }
     }
   }
 }
\cs_set_eq:NN \getfactor \ioiooiioio_getfactor:Nnnn
\ExplSyntaxOff

\getfactor\test{\baselineskip}{f}{g}

\test

\bye

在此处输入图片描述

我不确定您是否想将这些尺寸相加;如果您想使用高度和深度之间的最大值,请将第二条\dim_to_fp:n命令中的行更改为

       \dim_max:nn { \fontcharht\font`#3 } { \fontcharht\font`#4 }
       +
       \dim_max:nn { \fontchardp\font`#3 } { \fontchardp\font`#4 }

答案4

@wipet 的回答解释了如何使用 e-TeX。使用相同方法的变体:

\makeatletter
\newcommand\SetToRatio[3]{% sets #1 to be the ratio #2/#3, where #2 and #3 
    % are lengths (registers or expressions).
    % The ratio #2/#3 should evaluate to less than 16384 in absolute value to 
    % avoid arithmetic overflow. It will be computed as fixed point
    % number with about 4 or 5 digits after decimal mark.
    \edef #1%
        {\strip@pt\dimexpr
         \numexpr\dimexpr#2\relax*65536/\dimexpr#3\relax\relax sp\relax}%
}
\makeatother

20000但请注意,这种方法存在缺陷,因为 20000pt大于 TeX 最大尺寸,所以无法得出结果,即比率 R,因此无法通过 生成\dimexpr。因此我们不能这样做

\SetToRatio{\foo}{20000sp}{1sp}

尽管论证完全合法,但结果20000却无法产生。

在我的另一个回答中https://tex.stackexchange.com/a/328894/4686,我编写了一个纯 Plain TeX 宏,它没有这个缺陷,并且比 Graphics 宏更精确。

答案包含最终舍入不同的变体,这里是变体 D。我使用 LaTeX 文档来方便网站访问者进行测试。该宏实际上称为\divdef,而不是\SetToRatio。它不使用任何包。

% j'avais fait cela dans https://tex.stackexchange.com/a/328894/4686

% Octobre 2016

\documentclass{article}

\makeatletter

\countdef\ddf@cnta=\z@
\countdef\ddf@cntb=\tw@
\countdef\ddf@cntc=4
\countdef\ddf@cntd=6

% % if used with Plain TeX, un-comment this
% % LaTeX loop or any loop allowing \else\repeat:
%
% \long\def\loop #1\repeat{%
%     \def \iterate {#1\relax \expandafter \iterate \fi}\iterate
%     \let \iterate \relax }

% \def\divdef #1#2#3{% if using plain tex

\newcommand\divdef [3]{%
  % description:
  % computes R = #2/#3 as nearest multiple of 1/65536 (ties go to even)
  % then define the macro #1 to be the decimal expansion of this up to five digits
  % after decimal mark.
  \begingroup
  \dimen@ #3\relax   % denominator
  \dimen@ii #2\relax % numerator
  \ifdim\dimen@<\z@
    \dimen@-\dimen@
    \dimen@ii-\dimen@ii
  \fi
  \ifdim\dimen@ii<\z@
    \def\ddf@sgn{-}\dimen@ii-\dimen@ii
  \else
    \let\ddf@sgn\empty % no \@empty in Plain !
  \fi
  \ddf@cnta\dimen@   % non negative denominator (we hope non zero...)
  \ddf@cntb\dimen@ii % non negative numerator
  \divide\ddf@cntb\ddf@cnta % integer part of ratio, will be stored in \ddf@cntd
  \ddf@cntd\ddf@cntb
  \multiply\ddf@cntb-\ddf@cnta % no overflow possible because TeX's division truncates
  \advance\ddf@cntb\dimen@ii   % now numerator in \ddf@cntb is < denom
  \count@\z@ % will store fractional part as a multiple of sp's
  \ifnum\ddf@cntb>\z@
   \ifnum\ddf@cnta>32768\relax
    \ddf@cnta65536\relax
    \loop
      \ddf@cntc\dimen@ % denominator
      \advance\ddf@cntc-\ddf@cntb
      \ifnum\ddf@cntb<\ddf@cntc
        \divide\ddf@cnta\tw@
        \advance\ddf@cntb\ddf@cntb
      \else
        \ifnum\ddf@cntb=\ddf@cntc
          \divide\ddf@cnta\tw@
          \advance\count@\ddf@cnta
          \ddf@cnta\z@ % abort the loop here
        \else
          \advance\count@\ddf@cnta % not same order as in previous branch!
          \divide\ddf@cnta\tw@
          \ddf@cnta-\ddf@cnta
          \advance\ddf@cntc\ddf@cntc
          \ddf@cntb\ddf@cntc
        \fi
      \fi
    \ifnum\ddf@cnta=\z@\else % signed quantity: can not do if foo>\z@ ...
    \repeat
    % it is possible here that \count@ is 65536
    % in case of a tie at the last unit the rounding was to even!
   \else
    % here denom <= 2^15=32768 (=0.5pt), hence 65536num <= 2^31 - 65536
    \multiply\ddf@cntb65536\relax
    % extra steps to do rounding
    \ddf@cntc\ddf@cnta
    \divide\ddf@cntc\tw@
    \advance\ddf@cntb\ddf@cntc % no overflow possible
    \ddf@cntc\ddf@cntb % need to keep copy for later branch
    \divide\ddf@cntc\ddf@cnta
    \count@\ddf@cntc
    \ifodd\ddf@cnta
      % odd denom, no tie possible
    \else
      \multiply\ddf@cntc\ddf@cnta
      \ifnum\ddf@cntb=\ddf@cntc 
        % implement "ties go to even", the rounding was "up"
        \ifodd\count@\advance\count@\m@ne\fi
      \fi
    \fi
    % to get \count@ 65536 we would need to have N/D >= 65535.5/65536 
    % i.e. N/D >= 1 - 1/131072, but N/D<= 1 - 1/D, D<=32768, hence  
    % despite the rounding this branch always produces \count@ < 65536.
   \fi
  \fi
  \dimen@\count@ sp\relax
  % (\the\count@) % debug check
  \expandafter\divdef@end\the\dimen@ #1%
}
\begingroup
\catcode`P 12
\catcode`T 12
\lowercase{\gdef\divdef@end #1.#2PT}#3{%
  \advance\ddf@cntd #1\relax % almost always #1=0
  \ifnum#2>\z@
    \edef\x{\endgroup\edef\noexpand#3{\ddf@sgn\the\ddf@cntd.#2}}%
  \else
    \ddf@cntd\ddf@sgn\ddf@cntd
    \edef\x{\endgroup\edef\noexpand#3{\the\ddf@cntd}}%
  \fi
  \x
}\endgroup

\begin{document}

\ttfamily

\divdef\FOO{20000sp}{1sp}

\meaning\FOO

\divdef\FOO{355pt}{113pt}

\meaning\FOO

\divdef\FOO{1pt}{7pt}

\meaning\FOO

\divdef\FOO{10000pt}{7pt}

\meaning\FOO

\divdef\FOO{1000000000sp}{7sp}

\meaning\FOO

\end{document}

在此处输入图片描述

正如您在最后一个例子中所看到的,这超出了其他方法的可能性。

相关内容