对于IEEE 754浮点,double z = x-y是否保证z + y == x?

问题描述 投票:4回答:3

我有一个问题,可以减少到这个问题陈述:

给定一系列双精度,其中每个都在[0, 1e7]范围内,修改最后一个元素,使得数字的总和恰好等于目标数。一系列双打已经与epsilon(1e-7)中的目标数相加,但它们不是==。


以下代码正在运行,但它是否可以保证适用于满足第一句中所述要求的所有输入?

public static double[] FixIt(double[] input, double targetDouble)
{
    var result = new double[input.Length];
    if (input.Length == 0) return result;

    double sum = 0;
    for (int i = 0; i < input.Length - 1; i++)
    {
        sum += input[i];
        result[i] = input[i];
    }

    double remainder = targetDouble - sum;
    result[result.Length - 1] = remainder;
    return result;
}

var arr1 = Enumerable.Repeat(Math.PI / 13, 13).ToArray();
var arr2 = FixIt(arr1, Math.PI);

Debug.Print(Math.PI.ToString("R")); //3.1415926535897931
Debug.Print(arr1.Sum().ToString("R")); //3.1415926535897922
Debug.Print(arr2.Sum().ToString("R")); //3.1415926535897931

该问题的先前版本询问了修改第一个元素,但修改最后一个元素将问题简化为已知总和和已知目标,只留下last = target-sum是否暗示sum+last == target的问题。

(当然没有NaN,对范围的限制意味着对last的某些限制也可能有所帮助。)

关于真正的问题:我们已经以各种方式多次出现这个问题,但我们目前正在尝试做的是减少由于线性编程求解器中的数值不稳定性而出现的浮点误差(Coin-OR CBC)。例如,有6个变量都必须在[0,X]范围内,并且变量之和也必须是X.由于数值不稳定,求解器偶尔会返回略微负值和不完全求和的值X.我们已经克服了负数问题 - 现在只是试图解决X问题的总和。 (是的,我们可能会有一些限制因我们改变结果而被拒绝,但确保这些数字总和为X具有更高的优先级,而其他约束则不那么重要。)

c# floating-point ieee-754
3个回答
7
投票

z = x-y;不保证z+y == x,并且找不到像z这样的z+y == x的问题并不总是有解决方法。证据如下。

我们假设IEEE-754二进制浮点运算具有舍入到最近,与偶数相关联。使用基本的64位格式,但结果适用于其他格式。请注意,64位格式使用53位有效数字,这意味着只能表示具有53或更少有效二进制数字的数字。

考虑目标x等于1 + 2-52。让y为2-53。然后,在z = x-y;之后,z+y == x评估为假。算术细节如下所示,但是:

  • z = x-y;z设置为1,然后z+y产生1,小于x
  • 如果我们将z增加到下一个可表示的数字1 + 2-52,那么z+y产生1 + 2-51,这大于x
  • 所以没有z的价值使z+y == x成真。

细节:

x-y的数学结果是1 + 2-53。由于这有54个有效位(从20到2-53),因此无法表示,并且x-y的计算结果必须舍入。两个最接近的数字是1和1 + 2-52。 tie-to-even规则产生前一个数字1,因为其有效数字的低位为0,而1 + 2-52的低位为1。

因此,z = x-y;z设置为1。

那么z + y的数学结果是1 + 2-53。如上所述,这四舍五入为1,因此z+y的计算结果为1.因此z+y == x比较1比1 + 2-52并产生假。

此外,没有z的值可以使比较成立。如果我们用最小的可用步长增加z,从1到1 + 2-52,那么z + y的数学和就是1 + 2-52 + 2-53。这是两个可表示的数字1 + 2-52和1 + 2-51之间的中间位置。前者的低位为1,后者的低位为0,因此该z+y的计算结果为1 + 2-51,当然不等于1 + 2-52。

浮点加法是弱单调的,因此没有z的值会为z+y产生1 + 2-52。


3
投票

不,它没有。这是一个具体的反例;用Python编码,但您可以轻松地在C#中重复相同的实验:

>>> x = 0.24999916553497312
>>> y =  1.0000153779983518
>>> z = -0.7500162124633787
>>> z == x - y
True
>>> z + y == x
False

这是一个小的反例,xyz都是积极的:

>>> x = 0.4500000000000001
>>> y = 0.20000000000000004
>>> z = 0.2500000000000001
>>> z == x - y
True
>>> z + y == x
False

1
投票

根据定义,浮点运算并不精确(除非你只处理整数(编辑正确性:最多253,即9007199254740992));你总会有四舍五入的差异。如果你想要四舍五入到人类期望的范围:使用decimal而不是double。如果你使用decimal做同样的事情,它将适用于任何非十进制数字病态的数字集合。

© www.soinside.com 2019 - 2024. All rights reserved.