【发布时间】:2015-02-14 20:57:21
【问题描述】:
我有一个单元测试,测试边界:
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void CreateExtent_InvalidTop_ShouldThrowArgumentOutOfRangeException()
{
var invalidTop = 90.0 + Double.Epsilon;
new Extent(invalidTop, 0.0, 0.0, 0.0);
}
public static readonly double MAX_LAT = 90.0;
public Extent(double top, double right, double bottom, double left)
{
if (top > GeoConstants.MAX_LAT)
throw new ArgumentOutOfRangeException("top"); // not hit
}
我以为我会通过向其添加最小可能的正双精度来将 90.0 倾斜到边缘,但现在没有抛出异常,知道为什么吗?
调试时,我看到 top 是 90,而它应该是 90.00000000.... 什么。
编辑:
我应该更努力地思考一下,90+Double.Epsilon 会失去它的分辨率。似乎最好的方法是做一些位移。
解决方案:
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void CreateExtent_InvalidTop_ShouldThrowArgumentOutOfRangeException()
{
var invalidTop = Utility.IncrementTiny(90); // 90.000000000000014
// var sameAsEpsilon = Utility.IncrementTiny(0);
new Extent(invalidTop, 0, 0, 0);
}
/// <summary>
/// Increment a double-precision number by the smallest amount possible
/// </summary>
/// <param name="number">double-precision number</param>
/// <returns>incremented number</returns>
public static double IncrementTiny(double number)
{
#region SANITY CHECKS
if (Double.IsNaN(number) || Double.IsInfinity(number))
throw new ArgumentOutOfRangeException("number");
#endregion
var bits = BitConverter.DoubleToInt64Bits(number);
// if negative then go opposite way
if (number > 0)
return BitConverter.Int64BitsToDouble(bits + 1);
else if (number < 0)
return BitConverter.Int64BitsToDouble(bits - 1);
else
return Double.Epsilon;
}
/// <summary>
/// Decrement a double-precision number by the smallest amount possible
/// </summary>
/// <param name="number">double-precision number</param>
/// <returns>decremented number</returns>
public static double DecrementTiny(double number)
{
#region SANITY CHECKS
if (Double.IsNaN(number) || Double.IsInfinity(number))
throw new ArgumentOutOfRangeException("number");
#endregion
var bits = BitConverter.DoubleToInt64Bits(number);
// if negative then go opposite way
if (number > 0)
return BitConverter.Int64BitsToDouble(bits - 1);
else if (number < 0)
return BitConverter.Int64BitsToDouble(bits + 1);
else
return 0 - Double.Epsilon;
}
这样就可以了。
【问题讨论】:
-
双精度是一件令人讨厌的事情,但是当比较 A 和 B 之间的最大偏差时,
Double.Epsilon,所以你可能没有以非常非常小的幅度小费。 -
这里有一篇有趣的文章:johndcook.com/blog/2012/01/05/double-epsilon-dbl_epsilon TL;DR 是“
Double.Epsilon没有你想象的那么有用!” -
布鲁斯·道森有a great series of articles on ULPs and comparing floats and doubles。他的示例代码往往是 C++,但文章大多是解释。
-
这对于您的目的可能很好(尽管如果您想对负值进行等效测试,我不确定它是否正常工作),但将其用于其他目的的人应该考虑:此函数在呈现无穷大、nan、最大值、零、非规范化或负数时表现合理,如果不是,你关心吗?
标签: c# unit-testing double double-precision epsilon