问题
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);
}
回答1:
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)?
回答2:
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:
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.
回答4:
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
.
来源:https://stackoverflow.com/questions/20222314/is-math-absx-double-epsilon-equivalent-to-math-absx-0d