3

我已经阅读了大量关于浮动误差和浮动近似等的内容。
问题是:我从未读过现实世界问题的答案。今天,我遇到了一个现实世界的问题。而且这真的很糟糕,我真的不知道如何逃脱。

看看这个例子:

    [TestMethod]
    public void TestMethod1()
    {
        float t1 = 8460.32F;
        float t2 = 5990;
        var x = t1 - t2;
        var y = F(x);

        Assert.AreEqual(x, y);
    }

    float F(float x)
    {
        if (x <= 2470.32F) { return x; }
        else { return -x; }
    }

x应该是2470.32。但实际上,由于舍入误差,其值为2470.32031
大多数时候,这不是问题。功能是连续的,一切都很好,结果有点偏离。
但是在这里,我们有一个不连续的函数,而且误差非常非常大。测试恰好在不连续点上失败。

如何处理不连续函数的舍入误差?

4

5 回答 5

6

这里的关键问题是:

  • 在某些情况下,当输入值发生微小变化时,该函数的输出值会发生较大(且显着)的变化。
  • 您将错误的输入值传递给函数。

正如您所写,“由于舍入误差,[x 的值] 为 2470.32031”。假设您可以编写任何您想要的代码——只需描述要执行的功能,专家程序员团队将在几秒钟内提供完整、无错误的源代码。你会告诉他们什么?

您提出的问题是,“我将向此函数传递一个错误的值 2470.32031。我想让它知道正确的值是别的东西,并为我没有通过的正确值提供结果,而不是我通过的不正确的值。”</p>

一般来说,这个问题是不可能解决的,因为无法区分何时将 2470.32031 传递给函数但 2470.32 是预期的,以及何时将 2470.32031 传递给函数和 2470.32031 是预期的。你不能指望电脑能读懂你的想法。当您传递不正确的输入时,您不能期望正确的输出。

这告诉我们的是,函数 F 内部没有解决方案是可能的。因此,我们必须缩小范围,看看更大的问题。您必须检查传递给 F 的值是否可以改进(以更好的方式或更高的精度或补充信息计算)或者问题的性质是否是这样的这些知识可以被纳入 F.

于 2013-07-16T15:22:18.187 回答
2

注意:这个答案与 Eric 的答案基本相同,
它只是启发了测试的观点,因为测试是规范的一种形式。

这里的问题是 testMethod1 不测试 F。
而是测试十进制数 8460.32 到浮点数和浮点数减法的转换是不精确的。
但这是测试的目的吗?
你只能说,在某些恶劣的条件下(接近不连续),输入上的一个小错误会导致输出上的一个大错误,所以测试可以表示这是一个预期的结果。

请注意,函数 F 几乎是完美的,除了浮点值 2470.32F 本身。
实际上,浮点近似值会将小数四舍五入(精确为 1/3200)。
所以答案应该是:

Assert.AreEqual(F(2470.32F), -2470.32F); /* because 2470.32F exceed the decimal 2470.32 */

如果您想测试这种低级别的需求,您将需要一个具有高(任意/无限)精度的库来执行测试。

如果您无法承受函数 F 的这种不精确性,那么 Float 是不匹配的。您必须找到另一个具有更高、任意或无限精度的实现。
由你来指定你的需求,testMethod1 应该比现在更好地明确这个规范。

于 2013-07-16T17:36:30.687 回答
1

如果您需要 8460.32 数字完全没有舍入错误,您可以查看 .NET Decimal类型,该类型是明确创建的,用于表示以 10 为基数的小数,而不会出现舍入错误。他们如何施展这种魔法超出了我的想象。

现在,我意识到这对您来说可能是不切实际的,因为浮点数可能来自某个地方并将其重构为 Decimal 类型可能做的太多了,但是如果您需要它对依赖于的不连续函数具有如此高的精度该值您将需要更精确的类型或一些数学技巧。也许有某种方法可以始终确保创建具有舍入误差的浮点数,使其始终小于实际数字?我不确定是否存在这样的事情,但它也应该可以解决您的问题。

于 2013-07-16T14:47:28.547 回答
1

您的应用程序中表示了三个数字,您通过将它们表示为浮点数来接受每个数字的不精确性。

所以我认为你可以合理地声称你的程序运行正常

(oneNumber +/- some imprecision ) - (another number +/- some imprecision) 
        is not quite bigger than another number +/- some imprecision

当在纸上以十进制表示形式查看时,它看起来是错误的,但这不是您实现的。数据的来源是什么?8460.32 的精确度如何?如果是 8460.31999 应该发生什么?8460.32001?原始值是否已知如此精确?

最后,如果您想更准确地建模,请使用不同的数据类型,如其他地方所建议的那样。

于 2013-07-16T15:04:21.920 回答
-1

我总是假设在比较浮点值时,由于舍入问题,需要一个小的误差范围。在您的情况下,这很可能意味着在您的测试方法中选择不太严格的值 - 例如,定义一个非常小的误差常数并从 x 中减去该值。这是一个与此相关的问题

编辑以更好地解决结论性问题:据推测,函数在不连续点上输出什么并不重要所以在它的任一侧稍微测试一下。如果它确实重要,那么你能做的最好的事情就是在那时允许函数的两个输出中的任何一个。

于 2013-07-16T14:53:36.190 回答