【问题标题】:Exact solutions for lib(ic)lib(ic) 的精确解决方案
【发布时间】:2015-06-25 19:16:19
【问题描述】:

使用 ECLiPSe Prolog 的 lib(ic) 我偶然发现了来自 David H. Bailey, "Resolving numerical anomalies in scientific computation." 的以下问题,Unum book 提到了这个问题。实际上,这只是其中的一部分。首先,让我根据(is)/2 制定方程式。 另外,请注意,所有这些十进制数字都以基数 2 浮点数精确表示(包括 IEEE)

ECLiPSe Constraint Logic Programming System [kernel]
...
Version 6.2development #21 (x86_64_linux), Wed May 27 20:58 2015
[eclipse 1]: lib(ic).
...
Yes (0.36s cpu)
[eclipse 2]: X= -1, Y = 2, Null is 0.80143857*X+1.65707065*Y-2.51270273.

X = -1
Y = 2
Null = 0.0
Yes (0.00s cpu)

所以这是真正 0.0(根本没有四舍五入)。但现在用$= 代替is 是一样的:

[eclipse 3]: X= -1, Y = 2, Null $= 0.80143857*X+1.65707065*Y-2.51270273.

X = -1
Y = 2
Null = 2.2204460492503131e-16__2.2204460492503131e-16
Yes (0.00s cpu)

此区间不包含 0.0。我知道区间算术通常有点过于近似,如下所示:

[eclipse 4]: 1 $= sqrt(1).

Delayed goals:
    0 $= -1.1102230246251565e-16__2.2204460492503131e-16
Yes (0.00s cpu)

但至少等式成立!但是,在第一种情况下,不再包括零。显然我没有理解一些东西。我也试过eval/1,但无济于事。

[eclipse 5]: X= -1, Y = 2, Null $= eval(0.80143857*X+1.65707065*Y-2.51270273).

X = -1
Y = 2
Null = 2.2204460492503131e-16__2.2204460492503131e-16
Yes (0.00s cpu)

Null不包括0.0是什么原因?


(在@jschimpf 出人意料的回答后编辑)

这是本书第 187 页的引文,我将其解释为数字被精确表示(现在划过)。

使用 {3,5} 环境,可以模拟 IEEE 精度的环境。输入值是完全可表示的。 ...
{-1, 2}
...
完成了这项工作,用少于 一半的位计算了精确的答案被...使用

否则声明页面 184 成立:

...

0.80143857 x + 1.65707065 y = 2.51270273

这些方程看起来确实很无辜。假设精确的十进制输入,这个
系统完全由 x = -1 和 y = 2 求解。

这是用 SICStus 的library(clpq) 重新检查的:

| ?- {X= -1,Y=2,
     A = 80143857/100000000,
     B = 165707065/100000000,
     C = 251270273/100000000,
     Null = A*X+B*Y-C}.
X =  -1,
Y = 2,
A = 80143857/100000000,
B = 33141413/20000000,
C = 251270273/100000000,
Null = 0 ? 
yes

所以 -1, 2 是精确解。


精确的公式

这是一个在输入系数中没有舍入问题的重新表述,但解决方案仍然只是 -∞...+∞。因此,微不足道的正确,但不可用。

[eclipse 2]: A = 25510582, B = 52746197, U = 79981812, 
C = 80143857, D = 165707065, V = 251270273,
A*X+B*Y$=U,C*X+D*Y$=V.

A = 25510582
B = 52746197
U = 79981812
C = 80143857
D = 165707065
V = 251270273
X = X{-1.0Inf .. 1.0Inf}
Y = Y{-1.0Inf .. 1.0Inf}


Delayed goals:
    52746197 * Y{-1.0Inf .. 1.0Inf} + 25510582 * X{-1.0Inf .. 1.0Inf} $= 79981812
    80143857 * X{-1.0Inf .. 1.0Inf} + 165707065 * Y{-1.0Inf .. 1.0Inf} $= 251270273
Yes (0.00s cpu)

【问题讨论】:

    标签: prolog floating-accuracy eclipse-clp interval-arithmetic clpr


    【解决方案1】:

    这里有几个问题共同造成混乱:

    1. 除了声明之外,示例中的三个常量 有双浮点数的精确表示。

    2. 初始示例不涉及舍入是不正确的。

    3. 第一个示例中看似正确的结果实际上是由于 幸运的舍入错误。其他计算顺序给出不同的结果。

    4. 给出最接近的双浮点表示的确切结果 常数,确实不是零,而是 2.2204460492503131e-16。

    5. 间隔算法只有在输入时才能给出准确的结果 是准确的,这里不是这种情况。常数必须是 扩大到包含所需小数的区间。

    6. 类似于 lib(ic) 提供的关系算术本质上是这样的 不保证特定的评估顺序。出于这个原因,四舍五入 错误可能与功能评估期间遇到的错误不同。 然而,对于给定的常数,结果将是准确的。

    下面会更详细一点。正如我将展示一些 使用 ECLiPSe 查询的要点,提前简要介绍一下语法:

    • 两个浮点数用双下划线分隔,如0.99__1.01 表示具有下限和上限的区间常数,在这种情况下 1 附近的数字。

    • 用一个下划线分隔的两个整数,例如3_4 用分子和分母表示一个有理常数,在这个 案例四分之三

    为了演示第(1)点,将浮点表示转换为 0.80143857 成理性。这给出了精确的分数 3609358445212343/4503599627370496,接近但不相同, 到预期的小数 80143857/100000000。浮点数 因此表示是精确的:

    ?- F is rational(0.80143857), F =\= 80143857_100000000.
    F = 3609358445212343_4503599627370496
    Yes (0.00s cpu)
    

    以下显示结果如何取决于评估顺序 (以上第 3 点;请注意,我已将原始示例简化为 摆脱不相关的乘法):

    ?- Null is -0.80143857 + 3.3141413 - 2.51270273.
    Null = 0.0
    Yes (0.00s cpu)
    
    ?- Null is -2.51270273 + 3.3141413 - 0.80143857.
    Null = 2.2204460492503131e-16
    Yes (0.00s cpu)
    

    顺序依赖证明会发生舍入错误(第 2 点)。对于那些熟悉浮点运算的人来说,其实很容易看出 添加-0.80143857 + 3.3141413 时,0.80143857 的两位精度 在调整操作数的指数时迷路了。事实上它是 这个幸运的舍入错误使 OP 得到了看似正确的结果!

    实际上,第二个结果相对于 常量的浮点表示。我们可以证明这一点 通过使用精确的有理算术重复计算:

    ?- Null is rational(-0.80143857) + rational(3.3141413) - rational(2.51270273).
    Null = 1_4503599627370496
    Yes (0.00s cpu)
    
    ?- Null is rational(-2.51270273) + rational(3.3141413) - rational(0.80143857).
    Null = 1_4503599627370496
    Yes (0.00s cpu)
    

    由于加法是用精确的有理数完成的,所以现在的结果是 与订单无关,因为1_4503599627370496 =:= 2.2204460492503131e-16, 这证实了上面获得的非零浮点结果(第 4 点)。

    区间算术在这方面有何帮助?它通过计算工作 包含真值的间隔,这样结果将始终 在输入方面要准确。所以拥有很重要 包含的输入区间(ECLiPSe 术语中的有界实数) 所需的真实值。这些可以通过编写它们来获得 显式向下,例如0.80143856__0.80143858; 通过从一个精确的数字转换,例如一个有理数使用 breal(80143857_100000000);或通过指示解析器自动 将所有浮点数扩展为有界实数区间,如下所示:

    ?- set_flag(syntax_option, read_floats_as_breals).
    Yes (0.00s cpu)
    
    ?- Null is -0.80143857 + 3.3141413 - 2.51270273.
    Null = -8.8817841970012523e-16__1.3322676295501878e-15
    Yes (0.00s cpu)
    
    ?- Null is -2.51270273 + 3.3141413 - 0.80143857.
    Null = -7.7715611723760958e-16__1.2212453270876722e-15
    Yes (0.00s cpu)
    

    两个结果现在都为零,很明显 结果的精度取决于评估顺序。

    【讨论】:

    • 因此,直接浮点值不应被解释为精确值,而应被解释为来自十进制表示时的间隔。
    • 目前,区间的语法也需要四舍五入,所以这是一个固有的问题:writeq(1.0000000000000000000000002__1.0000000000000000000000003). 1.0__1.0。相反,应该有一个representation_error 或类似的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多