我有一个问题,可以简化为以下问题陈述:
给出一系列双打,每个双打的范围为
[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
是否暗示{{ 1}}。
(当然没有NaN,并且对范围的限制也暗示对sum+last == target
的一些限制也可能会有所帮助。)
关于实际问题:我们已经通过多种方式使这个问题浮出水面,但是我们目前正在尝试的是减少由于数值而出现的浮点误差。线性规划求解器(Coin-OR CBC)中的不稳定性。例如,有6个变量都必须在[0,X]范围内,并且变量的总和也必须为X。由于数值的不稳定性,求解器有时会返回略微为负的值以及未求和的值不能精确等于X。我们已经克服了负数问题-现在只是试图解决X问题的总和。 (是的,可能有些约束因我们更改结果而无法遵循,但确保将这些数字的总和设为X的优先级更高,而其他约束并不那么重要。)
答案 0 :(得分: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
的计算结果为false。算术详细信息如下所示,但是:
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个有效位(从2 0 到2 −53 ),因此无法表示,因此x-y
的计算结果必须四舍五入。两个最接近的数字是1和1 + 2 −52 。平分关系规则产生前一个数字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 进行比较,并生成false。
此外,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 。
答案 1 :(得分:3)
不,不是。这是一个具体的反例;用Python编码,但是您可以轻松地在C#中重复相同的实验:
>>> x = 0.24999916553497312
>>> y = 1.0000153779983518
>>> z = -0.7500162124633787
>>> z == x - y
True
>>> z + y == x
False
下面是一个小的反例,其中x
,y
,z
均为肯定:
>>> x = 0.4500000000000001
>>> y = 0.20000000000000004
>>> z = 0.2500000000000001
>>> z == x - y
True
>>> z + y == x
False
答案 2 :(得分:1)
根据定义,浮点算术并不精确(除非您仅处理整数(正确性编辑:最大为2 53 ,即9007199254740992));您将总是有四舍五入的差异。如果希望四舍五入匹配人类的期望,请使用decimal
而不是double
。如果您对decimal
执行相同的操作,则对于十进制数字中非病态的任何数字集,它都将正确运行。