Является ли Math.Abs (x) <double.Epsilon эквивалентным Math.Abs (x) == 0d?
После небольшого чтения эта статья пробудила во мне интерес:
Я бы подумал, что да, эти два утверждения эквивалентны, учитывая заявление MSDN:
Представляет наименьшее положительное значение Double, которое больше нуля. Это поле является постоянным.
Любопытно посмотреть, что думают люди.
РЕДАКТИРОВАТЬ: нашел компьютер с 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);
}
4 ответа
Код IL, кажется, проливает свет на это.
Epsilon - это просто двойное число с дробной частью 1, знак 0, показатель степени 0. Ноль - двойное число с дробной частью 0, знак 0, показатель 0.
Согласно http://en.wikipedia.org/wiki/IEEE_754-1985, числа с плавающей запятой с одинаковым знаком и показателем степени сравниваются обычным образом, что означает, что (x < 1) совпадает с (x == 0).
Теперь, возможно ли получить ноль, который не является дробной = 0, экспонентой = 0 (нас не волнует знак, на месте есть Math.Abs)?
Да, насколько я могу судить, они должны быть эквивалентны. Это связано с тем, что никакая разница не может иметь величину меньше, чем эпсилон, а также быть ненулевой.
Моя единственная мысль касалась таких значений, как double.NaN, я проверял это, PositiveInfinity и т. Д., И результаты были одинаковыми. Кстати, сравнение double.NaN с числом возвращает false.
Я не уверен, что вы подразумеваете под "эквивалентом" здесь, поскольку это довольно расплывчатый термин.
Если вы имеете в виду, будет ли.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
В этом смысле, если вы как-то производите значение x
это меньше чем double.Epislon
, он уже будет храниться в памяти как нулевое значение, поэтому Abs(x)
просто будет Abs(0)
который, == 0d
,
Но это ограничение двоичного представления, которое используется в.NET для хранения чисел с плавающей запятой: оно просто не может представлять ненулевое число меньше double.Epsilon
так что округляет.
Это не означает, что эти два утверждения "эквивалентны", потому что они полностью зависят от контекста. Очевидно, что 4.94065645841247E-324 * 0.5
это не ноль, это 2.470328229206235e-324
, Если вы выполняете вычисления, требующие такого уровня точности, чем нет, они не эквивалентны - и вам также не повезло, пытаясь сделать их в C#.
В большинстве случаев значение double.Epsilon
слишком мал, чтобы иметь какую-либо ценность, а это означает, что Abs(x)
должен == 0d
для значений, намного превышающих double.Epison
, но C# полагается на вас, чтобы понять это; если его спросить, он с радостью выполнит расчеты с такой точностью.
К сожалению, заявлениеMath.Abs(x) < double.Epsilon
эквивалентно Math.Abs(x) == 0d
"это совсем не так для систем ARM.
MSDN на Double.Epsilon противоречит себе, заявив, что
В системах ARM значение константы Epsilon слишком мало для обнаружения, поэтому оно равно нулю.
Это означает, что в системах ARM нет неотрицательных двойных значений, меньших Double.Epsilon
так что выражение Math.Abs(x) < double.Epsilon
это просто еще один способ сказать false
,