Подтвердить что ты не робот

Когда есть система. Двойной не двойной?

После просмотра того, как double.Nan == double.NaN всегда является ложным в С#, мне стало любопытно, как было реализовано равенство под капотом. Поэтому я использовал Resharper для декомпиляции Double struct, и вот что я нашел:

public struct Double : IComparable, IFormattable, IConvertible, IComparable<double>, IEquatable<double>
{
    // stuff removed...

    public const double NaN = double.NaN;

    // more stuff removed...
}

Это указывает на то, что struct Double объявляет константу, определенную в терминах этого особого нижнего регистра Double, хотя я всегда думал, что эти два были полностью синонимичными. Что еще, если я перейду к реализации в нижнем регистре, Resharper просто прокручивает меня до объявления в верхней части файла. Аналогично, переход к реализации нижнего регистра NaN просто приводит меня к объявлению константы ранее в строке!

Итак, я пытаюсь понять это, казалось бы, рекурсивное определение. Это просто артефакт декомпилятора? Возможно, ограничение в Resharper? Или это нижний регистр двойной фактически другой зверь вообще - представляющий что-то на более низком уровне от CLR/CTS?

Откуда действительно NaN?

4b9b3361

Ответ 1

На сегодняшний день лучшим источником, который вы можете получить для сборников .NET, является фактический исходный код, который использовался для их создания. Избавляет любой декомпилятор за точность, комментарии могут быть весьма полезными. Загрузите Справочный источник.

Затем вы также увидите, что Double.NaN не определен в IL, как предположил Марк, он фактически находится в файле исходного кода С#. Файл исходного кода net/clr/bcl/system/double.cs показывает реальное объявление:

  public const double NaN = (double)0.0 / (double)0.0;

который использует компилятор С# для оценки постоянных выражений во время компиляции. Или, скажем, язык в щеке, NaN определяется компилятором С++, так как язык, который использовался для написания компилятора С#;)

Ответ 2

Остерегайтесь взглянуть на декомпилированный код, особенно если он для чего-то встроенного. Фактический IL здесь (для .NET 4.5, по крайней мере):

.field public static literal float64 NaN = float64(NaN)
{
    .custom instance void __DynamicallyInvokableAttribute::.ctor()
}

то есть. это обрабатывается непосредственно в IL через токен NaN.

Однако, поскольку это const (literal в IL), он будет "сожжен" на сайте вызова; в другом месте, использующем double.NaN, также будет использоваться float64(NaN). Аналогично, например, если я делаю:

const int I = 2;
int i = I;
int j = 2;

оба этих назначения будут выглядеть одинаково в финальном ИЛ (они оба будут ldc.i4.2).

Из-за этого большинство декомпиляторов распознает образец IL NaN и представляет его с эквивалентом языка double.NaN. Но это не означает, что код сам рекурсивный; они, вероятно, просто не проверяют "но это двойное. В конечном счете это просто частный случай, когда float64(NaN) является признанным значением в IL.

Кстати, рефлектор декомпилирует его как:

[__DynamicallyInvokable]
public const double NaN = (double) 1.0 / (double) 0.0;

Это опять же не означает, что это правда: p Просто, что это то, что может иметь один и тот же конечный результат.