我已经阅读了大量关于浮动误差和浮动近似的事情,以及所有这些 问题是:我从未读过现实世界问题的答案。而今天,我遇到了一个现实世界的问题。这真的很糟糕,我真的不知道如何逃避。
看一下这个例子:
[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
大多数时候,这不是问题。功能是连续的,一切都很好,结果有点偏差
但是在这里,我们有一个不连续的功能,错误真的很大。测试在不连续点上完全失败。
如何处理不连续函数的舍入误差?
答案 0 :(得分:6)
这里的关键问题是:
正如您所写,“由于舍入误差,[x的值]为2470.32031”。假设您可以编写任何您想要的代码 - 只需描述要执行的功能,专家程序员团队将在几秒钟内提供完整的,无错误的源代码。你会告诉他们什么?
你提出的问题是,“我要将错误的值2470.32031传递给此函数。我希望它知道正确的值是其他东西,并提供正确值的结果,我没有通过,而不是我传递的错误值。“
一般来说,这个问题是不可能解决的,因为无法区分2470.32031何时传递给函数,但2470.32是从2470.32031传递给函数和2470.32031的。你不能指望一台电脑能读懂你的想法。当您传递错误的输入时,您无法期望正确的输出。
这告诉我们的是函数F内部没有解决方案。因此,我们必须缩小并查看更大的问题。您必须检查传递给F的值是否可以改进(以更好的方式计算或以更高的精度计算或使用补充信息计算),或者问题的性质是否如此,当2470.32031通过时,总是打算2470.32,这样这些知识可以纳入F。
答案 1 :(得分:2)
注意:这个答案基本上与Eric的答案相同 它只是启发了测试的观点,因为测试是一种规范形式。
这里的问题是testMethod1不测试F.
而是测试十进制数8460.32到float和float减法的转换是不精确的
但这是测试的意图吗?
你可以说的是,在某些不良条件下(接近不连续),输入上的小错误会导致输出上出现大的错误,因此测试可能表明它是预期的结果。
请注意,函数F几乎是完美的,除了浮点值2470.32F本身 实际上,浮点近似将使十进制数超过(精确到1/3200) 所以答案应该是:
Assert.AreEqual(F(2470.32F), -2470.32F); /* because 2470.32F exceed the decimal 2470.32 */
如果要测试这种低级别要求,则需要具有高(任意/无限)精度的库来执行测试。
如果你无法承担F函数的这种不精确性,那么Float是一个不匹配的问题,你必须找到另一个具有增加,任意或无限精度的实现。
由您来指定您的需求,testMethod1应该比现在更好地明确这个规范。
答案 2 :(得分:1)
如果您需要8460.32数字,而不需要舍入错误,您可以查看明确创建的.NET Decimal类型,以表示基数为10的小数而不会出现舍入错误。他们如何表现这种魔力超出了我的范围。
现在,我意识到这对你来说可能是不切实际的,因为浮动可能来自某个地方并且将它重构为Decimal类型可能太多了,但是如果你需要它来为不连续函数提供那么多的精度依赖于这个值,你需要一个更精确的类型或一些数学技巧。也许有一些方法可以始终确保创建一个具有舍入误差的浮点数,使其始终小于实际数字?我不确定这样的事情是否存在,但它也应该解决你的问题。
答案 3 :(得分:1)
你的申请表中有三个数字,你已经通过将它们表示为花车来接受每个数字的不精确。
所以我认为你可以合理地声称你的程序运行正常
(oneNumber +/- some imprecision ) - (another number +/- some imprecision)
is not quite bigger than another number +/- some imprecision
在纸上以十进制表示查看时看起来不对,但这不是您实施的内容。数据的来源是什么? 8460.32究竟有多精确?如果它是8460.31999应该发生什么? 8460.32001?原始值是否已知如此精确?
最后,如果您想要更高精度建模,请使用其他数据类型,如其他地方所建议的那样。
答案 4 :(得分:-1)
我总是假设在比较浮点值时,由于舍入问题,需要一小部分误差。在您的情况下,这很可能意味着在您的测试方法中选择不那么严格的值 - 例如,定义一个非常小的误差常数并从x中减去该值。这是与此相关的SO question。
编辑以更好地解决结束性问题:假设函数输出在完全的不连续性并不重要,因此在它的任何一侧稍微进行测试。如果它确实重要,那么你可以做的最好的事情就是允许该点的两个输出中的任何一个输出。