Первоначально я думал, что Math.Sign
будет правильным способом, но после запуска теста кажется, что он обрабатывает теги -0.0
и +0.0
.
Как я могу проверить отрицательный ноль?
Ответ 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% улучшение производительности при отладке. После компиляции в режиме выпуска и запуска из командной строки нет существенной разницы.
Я не мог генерировать повышение производительности, используя небезопасный код, но это может быть связано с моей неопытности.