我如何testing负零?

最初,我认为Math.Sign将是正确的路线,但在运行testing后,似乎将-0.0+0.0视为相同。

这是一个糟糕的破解方式:

 private static readonly long NegativeZeroBits = BitConverter.DoubleToInt64Bits(-0.0); public static bool IsNegativeZero(double x) { return BitConverter.DoubleToInt64Bits(x) == NegativeZeroBits; } 

基本上这是testing-0.0的确切位模式,但不必硬编码。

经过一番search之后,我终于把它做到了C#规范的7.7.2节 ,并提出了这个解决scheme。

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

负零具有设置的符号位。 从而:

  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抖动来说,这是非常典型的行为,它在优化浮点代码时不能很好地处理转angular情况。 在这方面,x64抖动要好得多。 虽然可以说没有比赋予负数零的含义更糟糕的angular落案例。 预先警告。

 x == 0 && 1 / x < 0 

这是另一个黑客。 它利用了这样一个事实,即一个struct上的Equals将进行按位比较,而不是在其成员上调用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

更一般地说,你可以做,

 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; } 

后者在debugging中提供了大约50%的性能提升。 一旦在释放模式下编译并从命令行运行, 则没有显着差异

我不能使用不安全的代码来提高性能,但这可能是由于我缺乏经验。