Exact solutions for lib(ic)

时间:2015-06-25 19:16:20

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

Using ECLiPSe Prolog's lib(ic) I stumbled upon the following problem from David H. Bailey, "Resolving numerical anomalies in scientific computation." which I was referred to by the Unum book. Actually, it is only part of it. First, let me formulate the equation in terms of (is)/2. Also, please note that all these decimal numbers have an exact representation in radix 2 floats (which comprises 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) So this is truly 0.0 (no rounding at all). But now the same with $= in place of 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) This interval does not contain 0.0. I am aware that interval arithmetic often is a bit too approximate as in: [eclipse 4]: 1 $= sqrt(1). Delayed goals: 0 $= -1.1102230246251565e-16__2.2204460492503131e-16 Yes (0.00s cpu) But at least the equation holds! However, in the first case zero is no longer included. Evidently I have not understood something. I tried also eval/1 but to no avail. [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) What is the reason for Null not including 0.0? (Edit after @jschimpf's surprising answer) Here is the quotation from the book page 187 that I interpreted as meaning that the numbers are represented exactly (now stroked through). Use a {3,5}, environment, the one that can simulate IEEE single precision. The input values are exactly representable. ...{-1, 2}...That did the job, computing the exact answer with fewer than half the bits used by ... Otherwise the statement page 184 holds: ... 0.80143857 x + 1.65707065 y = 2.51270273 The equations certainly look innocent enough. Assuming exact decimal inputs, thissystem is solved exactly by x = -1 and y = 2. Here is it rechecked with 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 So -1, 2 are the exact solutions. A precise formulation Here is a reformulation that does not have rounding problems in the input coefficients, still the solution is just -∞...+∞. Thus trivially correct, but not useable. [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)

1 个答案:

答案 0 :(得分:5)

这里有几个问题合谋造成混淆:

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

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

  3. 第一个例子中看似正确的结果实际上是由于a 幸运的舍入错误。其他计算订单会产生不同的结果。

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

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

  6. lib(ic)提供的关系算法本质上就是这样 不保证特定的评估订单。因此四舍五入 错误可能与功能评估期间遇到的错误不同。 然而,结果对于给定的常数是准确的。

  7. 以下更详细介绍。正如我将展示一些 使用ECLiPSe查询点,预先快速了解语法:

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

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

    要演示point(1),请转换float的表示形式 成为理性的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的两位精度 在调整操作数的指数时迷路。事实上它是 这个幸运的舍入错误给了他看似正确的结果!

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

    ?- 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)
    

    这两个结果现在都包含零,并且它变得明显如何 结果的精确度取决于评估顺序。