Is Math.Abs(x) < double.Epsilon equivalent to M

2019-08-10 06:48发布

After a bit of light reading, this article piqued my interest:

I'd have thought that yes, the two statements are equivalent, given MSDN's statement:

Represents the smallest positive Double value that is greater than zero. This field is constant.

Curious to see what people think.

EDIT: Found a computer with VS on and ran this Test. Turns out that yes, as expected, they're equivalent.

    [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);
    }

4条回答
\"骚年 ilove
2楼-- · 2019-08-10 07:29

Yes, as far as I can tell they should be equivalent. This is because no difference can have a magnitude less than epsilon and also be nonzero.

My only thought was concerning values such as double.NaN, I tested that and PositiveInfinity, etc. and the results were the same. By the way, comparing double.NaN to a number returns false.

查看更多
闹够了就滚
3楼-- · 2019-08-10 07:34

I'm not sure what you mean by "equivalent" here, as that's a pretty vague term.

If you mean, will .NET consider any value less than double.Epsilon to be equal to 0d, then yes, as the article you linked to clearly demonstrates. You can show this pretty easily:

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

In that sense, if you somehow produce a value of x that is less than double.Epislon, it will already be stored in-memory as a zero value, so Abs(x) will just be Abs(0) which is, == 0d.

But this is a limitation of the binary representation as used by .NET to hold floating point numbers: it simply can't represent a non-zero number smaller than double.Epsilon so it rounds.

That doesn't mean the two statements are "equivalent", because that's entirely context-dependent. Clearly, 4.94065645841247E-324 * 0.5 is not zero, it is 2.470328229206235e-324. If you are doing calculations that require that level of precision, than no, they are not equivalent -- and you're also out of luck trying to do them in C#.

In most cases, the value of double.Epsilon is entirely too small to be of any value, meaning that Abs(x) should == 0d for values much larger than double.Epison, but C# relies on you to figure that out; it will happily do the calculations down to that precision, if asked.

查看更多
Animai°情兽
4楼-- · 2019-08-10 07:45

Unfortunately, the statement "Math.Abs(x) < double.Epsilon is equivalent to Math.Abs(x) == 0d" is not true at all for ARM systems.

MSDN on Double.Epsilon contradicts itself by stating that

On ARM systems, the value of the Epsilon constant is too small to be detected, so it equates to zero.

That means that on ARM systems, there are no non-negative double values less than Double.Epsilon, so the expression Math.Abs(x) < double.Epsilon is just another way to say false.

查看更多
The star\"
5楼-- · 2019-08-10 07:46

IL code seems to cast some light on this.

Epsilon is simply a double number with the fraction part being 1, sign 0, exponent 0. Zero is a double number with the fraction part being 0, sign 0, exponent 0.

According to http://en.wikipedia.org/wiki/IEEE_754-1985, floating point numbers with the same sign and exponent are compared ordinally, which means that (x < 1) is the same as (x == 0).

Now, is it possible to get a zero that isn't fraction = 0, exponent = 0 (we don't care about sign, there's a Math.Abs in place)?

查看更多
登录 后发表回答