如何测试负零?

问题描述 投票:0回答:7

最初我认为

Math.Sign
是正确的方法,但经过测试后,似乎它对
-0.0
+0.0
的处理方式相同。

c# .net floating-point
7个回答
51
投票

这是一个糟糕的黑客方法:

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

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

基本上这是测试 -0.0 的精确位模式,但无需对其进行硬编码。


17
投票

经过一番搜索,我终于找到了 C# 规范的第 7.7.2 节,并提出了这个解决方案。

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

9
投票
负零设置了符号位。因此:

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

编辑:正如OP指出的,这在发布模式下不起作用。 x86 JIT 优化器认真对待 if() 语句并直接加载零,而不是加载

value。这确实更具性能。但这会导致负零丢失。需要对代码进行调整以防止出现这种情况:

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

顺便说一句,这是 x86 抖动的典型行为,它在优化浮点代码时不能很好地处理极端情况。 x64 抖动在这方面要好得多。尽管可以说没有比赋予负零意义更糟糕的极端情况了。预先警告。


9
投票
x == 0 && 1 / x < 0
    

8
投票
这是另一个技巧。它利用了这样一个事实,即

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

    


3
投票
更一般地说,你可以这样做,

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% 的性能提升。一旦以发布模式编译并从命令行运行

没有显着差异

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


0
投票

我有点困惑,在坐在这里超过 13 年(在撰写本文时)之后,这个问题有所有这些优秀(并且有些复杂)的答案,但不是最“直接”(IMO)的答案:

static bool IsNegativeZero(double d) => Double.IsNegative(d) && d == 0.0;
请注意,根据 IEEE 规范,-0.0 相当于 +0.0,因此无需编写 

d == -0.0

,尽管这也可以。

© www.soinside.com 2019 - 2024. All rights reserved.