【问题标题】:Float to Double conversion - Best assertion in a unit test?浮点到双精度转换 - 单元测试中的最佳断言?
【发布时间】:2012-12-19 12:46:13
【问题描述】:

鉴于陈述

float f = 7.1f;
double d = f;

我们可以在单元测试中断言什么关于 d?


例如这不起作用:

Console.WriteLine(d == 7.1d); // false
Console.WriteLine(d < 7.1d + float.Epsilon); // true by luck
Console.WriteLine(d > 7.1d - float.Epsilon); // false (less luck)

到目前为止我发现的最好的方法是将值转换回来:

float f2 = (float)d;
Console.WriteLine(f2 == f); // true

这和粗鲁的说法是一样的

Console.WriteLine(d == 7.1f); // 7.1f implicitly converted to double as above

这个问题不是关于一般的双精度和浮点精度,而是关于单元测试如何最好地描述 d 范围的实用问题。在我的例子中,d 是由轻量级代码生成生成的代码中发生的转换的结果。在测试这个代码生成时,我必须对这个函数的结果做出断言,这最终归结为上面的简单问题。

【问题讨论】:

  • 您使用的是哪个单元测试框架?您可能不一定要针对您得出的条件断言,例如Assert.IsTrue(condition),您也许可以使用例如Assert.AreEqual(value1, value2) 可以处理数字格式之间的等价。
  • 我仍然不确定您要在这里测试什么。 double 比 float 具有更高的精度,因此它们几乎不会彼此“相等”。
  • 如果你省略了用于初始化浮点数的常量中的最后一个 f,情况可能会更糟,请在此处查看精心制作的示例stackoverflow.com/questions/13276862/…

标签: c# floating-point double numeric


【解决方案1】:

您的“最佳方式”是断言您生成的代码返回的内容在float 的误差范围内7.1。这可能是您想要检查的内容,在这种情况下,请继续。

另一方面,您可能想要断言您生成的代码具体地返回将7.1f 转换为double 的结果,在这种情况下您可以这样做:

Console.WriteLine(d == (double)f);

这更严格 - 您的测试断言 d 在一个小范围内,而上述测试断言 d 是一个特定值。

这真的取决于您将使用d 做什么。如果不是精确值会出错,请测试精确值,但如果可以在值的float 内,请检查float

【讨论】:

  • 罗林,我就是这样。我希望我的代码将 7.1f 转换为双精度,所以我现在的断言是 d.Should().Be((double)7.1f);这使我的期望最清楚。谢谢你的意见。
【解决方案2】:

比较两个浮点值ibm sugests 以测试abs(a/b - 1) &lt; epsilon

一个 msnd 指出,Epsilon 属性反映了实例值为零时在数值运算或比较中有意义的最小正值。

所以实际上你应该检查

Math.Abs(d/(double)f) - 1) < float.Epsilon)

【讨论】:

  • 感谢 ibm 的提示:如果您不知道基础测量的规模,使用测试“abs(a/b - 1)
  • +1。是的,将 epsilon 添加到可能很大的数字上是没有意义的。它根本没有效果。这仅适用于定点数。
  • 您指向的文档由 IBM 发布,但被识别为由 IBM 以外的个人和公司撰写。因此,不清楚 IBM 是否提出了这个建议,就像图书出版商是否同意他们出版的作者所说的一切一样。基于此而指责 IBM 提倡这种草率做法并不完全公平。
  • @EricPostpischil 这到底有多“草率”?对我来说,这似乎是如何测试潜在问题的相当合理的近似值。
  • @RonLugge:马虎的原因太多太复杂,无法在评论中详述。可以这么说,这种技术以增加错误等于结果为代价减少了“错误”不相等的结果,值epsilon 通常不应该是这个答案所指的Epsilon,相对阈值不是如果错误源自最终结果以外的值,则使用正确的标准,此测试不适合该问题中可能测试完全相等的情况,并且软件通常应该设计为避免需要这些比较。
【解决方案3】:

(float) d == f.

另一个答案建议d == (double) f,但这是一个无用的测试,因为(double) f 执行与d = f 隐式执行的相同转换。所以这个断言唯一可以测试的是实现的某些方面是否被破坏(例如,编译器不正确地实现了其中一个转换并且以不同于另一个的方式),一些外部机制改变了df在赋值和断言之间,或者源代码被破坏,使得d 既不是double 也不是float,也不是任何可以准确保存f 值的类型,或者没有执行赋值d = f

一般来说,我们期望没有浮点错误,因为在浮点的每个正常实现中,从较窄的精度转换为相同基数的较宽精度没有错误,因为较宽的精度可以表示每个值更窄的精度可以。在不常见的情况下,较宽的浮点格式可能具有较小的指数范围。只有在这种情况下,或者在错误定义的浮点格式中,转换为更广泛的格式才会导致值发生变化。在这些情况下,执行相同的转换不会检测到更改。

相反,我们从较宽的格式转换回较窄的格式。如果df 不同,则此转换有可能检测到错误。例如,假设 f 包含 0x1p-1000,但由于某种原因,它无法以 d 的格式表示,因此它被舍入为零。然后(float) d == f 计算为(float) 0 == 0x1p-1000,然后是0 == 0x1p-1000,然后是false。此外,此测试可能会检测到与其他建议相同的错误:实现损坏、df 的更改、d 的错误类型以及 d = f 的缺失分配。

除此之外,您会尝试通过此处的断言检测哪些错误?

【讨论】:

  • 值得注意的是,从floatDecimal,或从doubleDecimal 的转换可能是有损的即使对于在两种格式中都可以精确表示的值。例如,将16777215f 转换为Decimal 会产生16777220 的值,尽管float 精确表示值16,777,215,并且Decimal 也可以保存该值。
  • @supercat:“……在每个正常的实现中,将……转换为相同基数的更广泛精度没有错误……”。
  • 确实不能指望float 的每个值在Decimal 中都是representable,因为它们使用不同的基数,当然不能指望转换准确地说,在目标格式没有表示所讨论的值的情况下。我的意图不是要与您矛盾,而是要强调,如果基数不同,即使是在新旧格式中精确表示的值也可能会发生奇怪的转换(也许记录了 16777215f 到 16777220m 的舍入,但至少可以这么说似乎很奇怪)。
  • @supercat:如果从floatDecimal 的转换产生了与原始float 不同的值,即使它在Decimal 中完全可以表示,那么执行转换的软件有缺陷。
  • 我上面的特定示例是有缺陷的,因为我一直使用错误的转换方法,直到数字变大才会出现真正的问题,但发生的是从Double 的转换至Decimal假设double 80345678.90000000000372529(这是12345678.8999999999999999999999999999999999999999999999999999999999999999999999999999916)的值为12345678.900000000013038516)是“更有可能”的意图旨在代表12345678.9比12345678.90000000000372529。不过,该例程相当草率,因为它完善了显然应该是相关的精度。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-12
  • 2018-02-23
  • 2014-05-29
  • 2018-04-26
相关资源
最近更新 更多