如何防止长度值运算的精度损失?

如何防止长度值运算的精度损失?

\MM等于1mm并且内部存储为2.84526pt。值\MMQUARTER等于0.25mm并且应该存储为0.711315pt( 2.84526/4 = 0.711315) 但是存储为0.7113pt。我需要找到一个解决方案来防止 被截断0.000015

\documentclass[border=5mm]{standalone}
\usepackage{tikz}

\begin{document}
    \begin{tikzpicture}

        \pgfmathsetlengthmacro{\MM}{1mm}
        \pgfmathsetlengthmacro{\MMQUARTER}{0.25mm}

        \path node[align=left]
            {%
                \pgfmathparse
                    {% 'MM' IS 2.84526
                        \MM
                    }   \pgfmathresult
                    % NEXT LINE CAN BE IGNORED
                    \hspace{0.33cm} = 1mm
                    \\ % NEW LINE

                \pgfmathparse
                    {% 'MMQUARTER' MUST BE 0.711315 BUT IS 0.7113
                        \MMQUARTER
                    }   \pgfmathresult
                    % NEXT 3 LINES CAN BE IGNORED
                    \hspace{0.5cm} $\approx$ 0.25mm;
                    \\ % NEW LINE
                    \hspace{1.5cm} must be 0.7113\textcolor{red}{15} (exactly 0.25mm)
            };

    \end{tikzpicture}
\end{document}

答案1

第一句话有点误导

值 \MM 等于 1mm,内部存储为 2.84526pt

TeX (几乎) 从不使用浮点运算,并且从不在 TeX 内部可检测到机器运算差异的地方使用它。

几乎所有的算术都是精确的整数算术,为了显示目的而进行十进制转换。

TeX 使用转换 25.4mm=1in=72.27pt, 65536sp=1pt

所以 1mm 是 186467sp,这个整数就是长度

除以 4 并向下舍入得到 46616sp,显示为 0.7113pt。

您可以通过在命令行上运行 tex 以交互方式查看这些值:

$ tex
This is TeX, Version 3.14159265 (TeX Live 2017) (preloaded format=tex)
**\dimen0=1mm

*\count0=\dimen0

*\showthe\count0
> 186467.
<*> \showthe\count0

? 

*\divide\count0 by 4

*\showthe\count0
> 46616.
<*> \showthe\count0

? 

*\dimen0=\count0 sp

*\showthe\dimen0
> 0.7113pt.
<*> \showthe\dimen0

? x
No pages of output.
Transcript written on texput.log.

答案2

让我首先评论一下你代码中的注释

    \pgfmathsetlengthmacro{\MM}{1mm}
    \pgfmathsetlengthmacro{\MMQUARTER}{0.25mm}

                {% 'MM' IS 2.84526
                    \MM

                {% 'MMQUARTER' MUST BE 0.711315 BUT IS 0.7113
                    \MMQUARTER

我认为\pgfmathsetlengthmacro在某个时候,这在内部可以归结为标准 TeX。像这样的尺寸规范0.25mm就是这样解析的。我将用术语来描述它,不是逐一对应 TeX 处理,而是产生完全相同的结果。

  1. 首先0.25圆形为整数倍N/65536。此处为精确匹配N=16384

  2. 然后应用转换因子,对于单位来说,转换因子mm是(根据tex.web §4587227/2540。这意味着 TeX 评估截断为整数的精确分数16384*7227/2540

    现在:

    $ rlwrap etex
    This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/MacPorts 2017_1) (preloaded format=etex)
     restricted \write18 enabled.
    **xintexpr.sty
    entering extended mode
    (/usr/local/texlive/2017/texmf-dist/tex/generic/xint/xintexpr.sty
    (/usr/local/texlive/2017/texmf-dist/tex/generic/xint/xintfrac.sty
    (/usr/local/texlive/2017/texmf-dist/tex/generic/xint/xint.sty
    (/usr/local/texlive/2017/texmf-dist/tex/generic/xint/xintcore.sty
    (/usr/local/texlive/2017/texmf-dist/tex/generic/xint/xintkernel.sty))))
    (/usr/local/texlive/2017/texmf-dist/tex/generic/xint/xinttools.sty))
    *\message{\xinttheiexpr [40] 16384*7227/2540\relax}
    46616.9952755905511811023622047244094488188976 
    *\bye
    No pages of output.
    Transcript written on xintexpr.log.
    

    所以在这种情况下 TeX 将会获得46616,我们看到它在这里犯了一个相对较大的错误。

  3. 结论是,0.25mm在内部存储为,46616sp其中1sp65536th(TeX) 的一部分pt

有关详细信息,请参阅为何pdf文件无法复制?

46616sp那么,当使用 显示内部存储为 的尺寸时会发生什么\the?该算法是§103并且tex.web有点微妙。我们想要生成一个pt单位值,它将精确地重现46616spif圆形的整数倍1/65536。当然,我们首先处理整数部分,这里整数部分消失了。因此,我们假设我们有一些初始值n。Knuth0 <= n < 65536实际上(间接地)考虑了 的十进制展开(n + 0.5)/65536。假设我们查看K这个十进制展开的前几位数字,因此,对于K = 1, 2, 3, 4并且最多5,我们有

(n+0.5)/65536 = D_K/10^K + N_K/(10^K \times 65536)

对于N_K那些K被认为是整数,正数,且严格小于65536(二乘法的考虑证明了我的一些断言)。N_K只需重复乘以10模即可获得65536。上面的等式给出

65536 D_K/10^K = n + 0.5 - N_K/10^K

如果对于任意一个K我们观察到0< N_K <= 10^K(已知正性),则四舍五入的将按预期65536 D_K/10^K返回。这可能发生在、... 或。想象一下它没有发生。那么它必然会在但这种情况是特别处理的。如果我们达到它是因为我们有。然后让我们。它是,所以我们写其中是数字。简化后我们得到nK = 14K = 5K = 5N_4 > 10000M_5 = 10 N_4 - 50000 + 32768> 65536M_5 = q 65536 + rq

n = 65536 (10 D_4 + q)/100000 + (r - 32768)/100000

并且D_5 = 10 D_4 + q具有第五位数字,我们再次得到一个小数,将其四舍五入D_5/100000为的整数倍得到。(我将不得不再次阅读但这是我对它的印象)。1/65536n/65536§103tex.web

我们可以按照 的步骤进行操作n = 46616。首先我们计算10(46616.5) = 466165 = 7x65536+7413给出N_1 = 7413。它不是<= 10,因此算法继续。第一个数字是7

Python 3.5.4 |Continuum Analytics, Inc.| (default, Aug 14 2017, 12:43:10) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> divmod(74130,65536)
(1, 8594)
>>> divmod(85940,65536)
(1, 20404)
>>> divmod(204040,65536)
(3, 7432)

因此数字为7113,这里我们得到N_4 = 7432小于10000。算法在这里停止,因此 TeX 生成0.7113pt

从上面的描述中,你可以看到它永远不会产生小数点后 6 位数字,例如0.711315。要获得这一点,你必须使用数学引擎(可能在宏中实现;因为 1999 年的 e-TeX\numexpr更容易扩展地做到这一点,至少在理论上是这样)能够处理这样的十进制数。

您可以使用\xinttheiexpr [D]宏进行此类计算,其中D是所要求的最终结果小数点后的数字位数,但计算是精确完成的。


回到四分之一毫米(使用 Plain TeX,在 LaTeX 中也可以使用\usepackage{xintexpr}):

\input xintexpr.sty

\xintdefvar mm:= 7227/2540;

\edef\MYQUARTERMM{\xinttheexpr trunc(0.25mm, 40)\relax}

\show\MYQUARTERMM

\bye

在日志中生成

> \MYQUARTERMM=macro:
->0.7113188976377952755905511811023622047244.
l.7 \show\MYQUARTERMM

顺便说一句,这表明0.711315原问题中出现的“as”的含义并不那么清楚。

从上可知,先验更接近的 6 位近似值是0.711319pt。经过检查,我们可以看出和都0.711319pt0.711315ptTeX 转换为内部表示46617sp\number\dimexpr 0.711319pt\relax\number\dimexpr 0.711315pt\relax)。

\the\dimexpr 0.711319pt\relax输出0.71132pt。因此,这0.71132pt是最准确的 5 位数字表示(经过测试,它是唯一46617sp内部给出的)实际精确0.25mm尺寸。但是如上所述,当 TeX 遇到 时,0.25mm它将获得46616sp。使用更大的cm单位会导致0.025cm更大的精度损失(46605sp)。

相关内容