C# 为什么将double.epsilon添加到一个值会导致相同的值,完全相等?
我有一个单元测试,测试边界:C# 为什么将double.epsilon添加到一个值会导致相同的值,完全相等?,c#,unit-testing,double,double-precision,epsilon,C#,Unit Testing,Double,Double Precision,Epsilon,我有一个单元测试,测试边界: [TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void CreateExtent_InvalidTop_ShouldThrowArgumentOutOfRangeException() { var invalidTop = 90.0 + Double.Epsilon; new Extent(invalidTop, 0.0, 0.0, 0.0);
[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添加最小可能的正数double来使其超出边缘,但现在没有抛出异常,知道为什么吗
在调试时,我看到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;
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
公共无效CreateExtent\u InvalidTop\u应通过GumentAutoFrangeException()进行
{
var invalidTop=Utility.IncrementTiny(90);/90.000000000000014
//var sameAsEpsilon=Utility.IncrementTiny(0);
新范围(invalidTop,0,0,0);
}
///
///以尽可能小的数量增加一个双精度数字
///
///双精度数
///递增数
公共静态双增量极小(双倍数字)
{
#区域健全性检查
if(Double.IsNaN(数字)| Double.isfinity(数字))
抛出新ArgumentOutOfRangeException(“数字”);
#端区
var位=位转换器。DoubleToInt64位(数字);
//如果是负数,则相反
如果(数字>0)
返回BitConverter.Int64BitsToDouble(位+1);
否则如果(数字<0)
返回BitConverter.Int64BitsToDouble(位-1);
其他的
返回双ε;
}
///
///以尽可能小的量减少双精度数字
///
///双精度数
///递减数
公共静态双递减率(双倍数字)
{
#区域健全性检查
if(Double.IsNaN(数字)| Double.isfinity(数字))
抛出新ArgumentOutOfRangeException(“数字”);
#端区
var位=位转换器。DoubleToInt64位(数字);
//如果是负数,则相反
如果(数字>0)
返回BitConverter.Int64BitsToDouble(位-1);
否则如果(数字<0)
返回BitConverter.Int64BitsToDouble(位+1);
其他的
返回0-Double.Epsilon;
}
这就完成了任务。根据:
Epsilon属性的值反映了最小的正值
Double
数值运算或比较中重要的值
当Double
实例的值为零时
(我的重点。)
将其添加到90.0不会产生“90.0之后的下一个最小值”,这只会再次产生90.0。因为Double.Epsilon是一个双倍数中“最小的明显变化”(松散地说) 。。但这并不意味着你使用它时它会有任何效果 正如您所知,浮动/双精度在其分辨率上有所不同,这取决于它们包含的vlue的大小。例如,人工:
- -100->+-0.1
- -10->+-0.01
- 0->+-0.001
- 10->+-0.01
- 100->+-0.1
如果分辨率是这样的,ε将是
0.001
,因为它是最小的可能变化。但是在这样的系统中,1000000+0.001
的预期结果是什么?Double.Epsilon
是最小的正代表值。仅仅因为它本身是可表示的,并不意味着它是任何其他可表示值和下一个最高值之间的最小值
假设你有一个只表示整数的系统。您可以将任何整数表示为5位有效数字,并使用刻度(例如,范围为1-100)
例如,这些值是完全可以表示的
- 12345(数字=12345,刻度=0)
- 12345000(数字=12345,刻度=3)
注意到,较大的值也具有相同的属性,例如,如果<代码> x < /C++ >是非常大的<代码>双< /代码>,则代码> X+1 可能等于
中。我不知道C#是否有任何等价物,但如果有,我希望它有一个类似的名称。math.h
Double.Epsilon
,所以你可能没有以非常小的幅度给它足够的倾斜;DR是“Double.Epsilon
没有你想象的那么有用!”布鲁斯·道森说。他的示例代码往往是C++,但文章大多是解释性的。这可能对您的目的很好(虽然我不确定它是否正确地工作,如果您想做一个负值的等价测试),但是人们为了适应其他目的而应该考虑:这个函数在无穷大的情况下是否合理地运行,maxvalue、零、非规范化或负数,如果不是,你在乎吗?在任何情况下都没有一个数字可以工作double
的最大精度为15位,因此,如果将一个很小的数字加在1上,结果是“不同的”double
不能加在1000000上,但仍然会得到一个“不同的”double
@Muhammad:这是一个独立的问题。参见和,“Double.Epsilon是一个双精度数字中最小的明显变化”不完全正确-它是最小的双精度g