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

Как я могу проверить отрицательный ноль?

Первоначально я думал, что Math.Sign будет правильным способом, но после запуска теста кажется, что он обрабатывает теги -0.0 и +0.0.

4b9b3361

Ответ 1

Вот громоздкий способ сделать это:

private static readonly long NegativeZeroBits =
    BitConverter.DoubleToInt64Bits(-0.0);

public static bool IsNegativeZero(double x)
{
    return BitConverter.DoubleToInt64Bits(x) == NegativeZeroBits;
}

В основном это тестирование для точного шаблона бит -0.0, но без необходимости его жесткого кодирования.

Ответ 2

После небольшого поиска я наконец добрался до раздела 7.7.2 спецификации С# и придумал это решение.

private static bool IsNegativeZero(double x)
{
    return x == 0.0 && double.IsNegativeInfinity(1.0 / x);
}

Ответ 3

Отрицательный ноль имеет установленный битовый бит. Таким образом:

    public static bool IsNegativeZero(double value) {
        if (value != 0) return false;
        int index = BitConverter.IsLittleEndian ? 7 : 0;
        return BitConverter.GetBytes(value)[index] == 0x80;
    }

Изменить: как указывал OP, это не работает в режиме Release. Оптимизатор x86 JIT принимает инструкцию if() серьезно и загружает нуль напрямую, а не загружает значение. Что действительно более результативно. Но это приводит к потере отрицательного нуля. Код необходимо отменить для предотвращения этого:

    public static bool IsNegativeZero(double value) {
        int index = BitConverter.IsLittleEndian ? 7 : 0;
        if (BitConverter.GetBytes(value)[index] != 0x80) return false;
        return value == 0;
    }

Это довольно типичное поведение для джиттера x86 btw, оно отлично справляется с угловыми случаями, когда оно оптимизирует код с плавающей точкой. В этом отношении джиттер x64 намного лучше. Хотя, возможно, нет худшего аргумента в углу, чем придание значения отрицательному нулю. Будьте предупреждены.

Ответ 4

x == 0 && 1 / x < 0

Ответ 5

Вот еще один хак. Он использует тот факт, что Equals на struct будет выполнять побитовое сравнение вместо вызова Equals для своих членов:

struct Negative0
{
    double val;
    public static bool Equals(double d)
    {
        return new Negative0 { val = -0d }.Equals(new Negative0 { val = d });
    }
}

Negative0.Equals(0); // false
Negative0.Equals(-0.0); // true

Ответ 6

В более общем плане вы можете сделать,

bool IsNegative(double value)
{
    const ulong SignBit = 0x8000000000000000;
    return ((ulong)BitConverter.DoubleToInt64Bits(value) & SignBit) == SignBit;
}

или, если хотите,

[StructLayout(LayoutKind.Explicit)]
private struct DoubleULong
{
    [FieldOffset(0)]
    public double Double;

    [FieldOffset(0)]
    public readonly ulong ULong;
}

bool IsNegative(double value)
{
    var du = new DoubleULong { Double = value };
    return ((du.ULong >> 62) & 2) == 2;
}

Более поздняя версия дает примерно 50% улучшение производительности при отладке. После компиляции в режиме выпуска и запуска из командной строки нет существенной разницы.

Я не мог генерировать повышение производительности, используя небезопасный код, но это может быть связано с моей неопытности.