【问题标题】:Is Math.Abs(x) < double.Epsilon equivalent to Math.Abs(x) == 0d?Math.Abs​​(x) < double.Epsilon 是否等同于 Math.Abs​​(x) == 0d?
【发布时间】:2013-12-11 21:43:29
【问题描述】:

稍稍阅读后,this article 引起了我的兴趣:

我认为是的,这两个语句是等价的,鉴于 MSDN 的声明:

表示大于零的最小正双值。该字段是常量。

很想知道人们的想法。

编辑:找到一台打开 VS 的计算机并运行此测试。事实证明,是的,正如预期的那样,它们是等价的。

    [Test]
    public void EpsilonTest()
    {
        Compare(0d);
        Compare(double.Epsilon);
        Compare(double.Epsilon * 0.5);
        Compare(double.NaN);
        Compare(double.PositiveInfinity);
        Compare(double.NegativeInfinity);
        Compare(double.MaxValue);
        Compare(double.MinValue);
    }

    public void Compare(double x)
    {
        Assert.AreEqual(Math.Abs(x) == 0d, Math.Abs(x) < double.Epsilon);
    }

【问题讨论】:

  • 你试过了吗?你的发现是什么?
  • 这样比较的目的是什么?这不会帮助您避免舍入错误。 Epsilon 太小了。
  • @Alan:那是相当不完整的。根据您正在使用的实际值的大小,这可能就足够了。当你在数十亿的数量级时,它可能还不够。当您在逗号后面大约 300 个位置时,它可能会。
  • @JeroenVannevel MSDN 引用 - 如果您创建自定义算法来确定两个浮点数是否可以被视为相等,我们不建议您将算法基于 Epsilon 常数的值来建立被认为相等的两个值的可接受的绝对差值。 (通常,差异幅度是 Epsilon 的许多倍。)
  • @Alan:这正是我的观点。如果您以 10^9 的顺序工作,则余量会太大。如果您在 10^-300 中工作就足够了,因为差异实际上接近 Epsilon。

标签: c# math double epsilon


【解决方案1】:

很遗憾,“Math.Abs(x) &lt; double.Epsilon 等同于 Math.Abs(x) == 0d”这一说法对于 ARM 系统根本不成立。

MSDN on Double.Epsilon 自相矛盾地声明

在 ARM 系统上,Epsilon 常数的值太小而无法检测,因此它等于零。

这意味着在 ARM 系统上,没有个小于 Double.Epsilon 的非负双精度值,所以表达式 Math.Abs(x) &lt; double.Epsilon 只是表达 false 的另一种方式。

【讨论】:

    【解决方案2】:

    IL 代码似乎对此有所了解。

    Epsilon 只是一个双精度数,小数部分为 1,符号为 0,指数为 0。 零是一个双精度数,小数部分为 0,符号为 0,指数为 0。

    根据http://en.wikipedia.org/wiki/IEEE_754-1985,按序比较符号和指数相同的浮点数,即(x

    现在,是否有可能得到一个不是分数 = 0、指数 = 0 的零(我们不关心符号,有一个 Math.Abs​​)?

    【讨论】:

      【解决方案3】:

      我不确定您在这里所说的“等效”是什么意思,因为这是一个非常模糊的术语。

      如果您的意思是,.NET 是否会认为任何小于 double.Epsilon 的值等于 0d,那么是的,正如您链接到的文章清楚地表明的那样。你可以很容易地展示它:

      var d1 = 0d;
      var d2 = double.Epsilon * 0.5;
      Console.WriteLine("{0:r} = {1:r}: {2}", d1, d2, d1.Equals(d2));
      // Prints: 0 = 0: True
      

      从这个意义上说,如果您以某种方式生成一个小于double.Epislon 的值x,它将已经作为零值存储在内存中,所以Abs(x) 将只是Abs(0),即== 0d

      但这是 .NET 使用二进制表示来保存浮点数的一个限制:它根本不能表示小于 double.Epsilon 的非零数,所以它是四舍五入的。

      这并不意味着这两个语句是“等价的”,因为这完全取决于上下文。显然,4.94065645841247E-324 * 0.5 不是零,而是2.470328229206235e-324。如果您正在执行需要该精度级别的计算,那么它们是不等价的——而且您尝试在 C# 中执行它们也很不走运。

      在大多数情况下,double.Epsilon 的值太小而不能具有任何值,这意味着对于比 double.Epison 大得多的值,Abs(x) 应该是 == 0d,但 C# 依赖于你来解决这个问题;如果被问到,它会很乐意将计算精确到该精度。

      【讨论】:

      • 通常,当询问两个表达式是否“等价”时,问题的意思是“这些表达式是否为每个可能的输入产生相同的输出?”在那篇文章中,这个问题的答案似乎是,是的,它们是等价的。
      • 是的,在 .NET 的范围内,这两个表达式应该总是产生相同的布尔结果。
      • 严格来说(吹毛求疵,抱歉),最终还是取决于 CPU 上的实现。如果它符合 IEEE 754,那么它应该以这种方式运行。例如,在我的 cpu 上,比较是由 CPU 指令 fcomip 完成的(在这两种情况下)。然后,有ja(如果高于则跳转)或je(如果相等则跳转)。因此,如果有一个(部分 - 它是一个小挑剔的)不兼容的 CPU,唯一可以修复它的将是 .NET JIT 编译器将两者都转换为je。很抱歉吹毛求疵:))
      【解决方案4】:

      是的,据我所知,它们应该是等价的。这是因为没有任何差异的大小可以小于 epsilon 并且不为零。

      我唯一的想法是关于诸如double.NaN之类的值,我测试了它和PositiveInfinity等,结果是一样的。顺便说一句,将 double.NaN 与数字进行比较会返回 false。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-02
        • 2014-02-14
        • 1970-01-01
        • 1970-01-01
        • 2019-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多