Является ли 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,

Другие вопросы по тегам