如何测试负零?

时间:2011-01-19 19:36:22

标签: c# .net floating-point

最初我认为Math.Sign是正确的方法,但在运行测试后,似乎它会同时处理-0.0+0.0

6 个答案:

答案 0 :(得分:44)

这是一种糟糕的黑客攻击方式:

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

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

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

答案 1 :(得分:14)

经过一番搜索后,我终于将其发送到了C#规范的Section 7.7.2,并提出了这个解决方案。

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

答案 2 :(得分:8)

负零已设置符号位。因此:

    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 jitter btw的典型行为,它在优化浮点代码时不能很好地处理极端情况。在这方面,x64抖动要好得多。虽然可以说没有比给负零的意义更糟糕的角落情况。预先警告。

答案 3 :(得分:8)

x == 0 && 1 / x < 0

答案 4 :(得分:7)

这是另一个黑客。它充分利用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

答案 5 :(得分:1)

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

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%的性能提升但是。在发布模式下编译并从命令行运行后,没有显着差异

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