2

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);
    }
trilson86
  • 939
  • 1
  • 9
  • 20
  • 2
    Have you tried it? What were your findings? – Jeroen Vannevel Nov 26 '13 at 16:16
  • What is the purpose of such a comparison? This won't help you avoid rounding errors. Epsilon is too small. – Alan Nov 26 '13 at 16:18
  • @Alan: that's rather uncomplete. It might suffice depending on the size of the actual values you're working with. When you're in the order of billions it probably won't suffice. When you're in the order of 300 places behind the comma, it might. – Jeroen Vannevel Nov 26 '13 at 16:19
  • @JeroenVannevel MSDN quote - If you create a custom algorithm that determines whether two floating-point numbers can be considered equal, we do not recommend that you base your algorithm on the value of the Epsilon constant to establish the acceptable absolute margin of difference for the two values to be considered equal. (Typically, that margin of difference is many times greater than Epsilon.) – Alan Nov 26 '13 at 16:23
  • @Alan: which is exactly my point. If you're working in 10^9 order, it will have a too big margin. If you're working in 10^-300 it will suffice because the difference is actually close to Epsilon. – Jeroen Vannevel Nov 26 '13 at 16:25
  • I should point out that there is no exact purpose for this. It is simply a case of curiosity. How would one _reliably_ test this? – trilson86 Nov 26 '13 at 16:27
  • @JeroenVannevel Actually, I don't think it will. double.Epsilon says it is 4.94065645841247e-324, leaving you about 24 decimal places past 10^-300 which is more than the about 15 significant digits or so of a double. The amount of rounding you'll get depends on how you are doing your calculations. Intuitively, epsilon is the most extreme smallest number that a double can represent, it therefore isn't going to be that useful it determining rounding errors of larger numbers. It would be hard to imagine a scenario in which you were only using numbers within epsilon amount of error. – Alan Nov 26 '13 at 16:28
  • see http://stackoverflow.com/questions/19837576/comparing-floating-point-number-to-zero/ – Exceptyon Nov 26 '13 at 16:53

4 Answers4

4

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)?

Luaan
  • 62,244
  • 7
  • 97
  • 116
1

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.

Alan
  • 7,875
  • 1
  • 28
  • 48
1

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.

Michael Edenfield
  • 28,070
  • 4
  • 86
  • 117
  • Normally, when asking if two expressions are 'equivalent,' the question means "Do these expressions produce the same output for every possible input?" In that reading, the answer to this question seems to be that, yes, they are equivalent. – reirab Nov 26 '13 at 17:01
  • that is true, within the limits of .NET those two expression should always produce the same boolean result. – Michael Edenfield Nov 26 '13 at 18:22
  • 1
    Strictly speaking (nitpicking, sorry), in the end, it's up to the implementation on the CPU. If it's IEEE 754 compliant, then it should behave this way. For example, on my cpu, the comparison is done by the CPU instruction `fcomip` (in both cases). Then, there's either `ja` (Jump if Above) or `je` (Jump if Equal). So if there's a (partially - it's a tiny nitpick) non-compliant CPU, the only one who could fix it would be the .NET JIT compiler converting both to `je`. Sorry for nitpicking :)) – Luaan Nov 27 '13 at 10:17
0

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.

ForNeVeR
  • 6,726
  • 5
  • 24
  • 43