如何测试负零?

Cha*_*ion 39 .net c# floating-point

最初我认为Math.Sign这是正确的方法,但在运行测试之后,似乎它对待-0.0+0.0相同.

Jon*_*eet 46

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

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

public static bool IsNegativeZero(double x)
{
    return BitConverter.DoubleToInt64Bits(x) == NegativeZeroBits;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 不得不查看"grotty" (14认同)
  • 看来你的解决方案比我的快25倍. (4认同)
  • 它并不是真的很糟糕,因为(a)它封装得很好,(b)如果用户不能依赖IEEE浮点数,那么她就不能依赖-0.0甚至是现有的.更好的抽象是`bool IsNegative(double x){return BitConverter.DoubleToInt64Bits(x)<0;}`.这是一个实际应该在标准`Math`类中的函数. (3认同)

Cha*_*ion 14

经过一些搜索后,我终于进入了C#规范的7.7.2节,并提出了这个解决方案.

private static bool IsNegativeZero(double x)
{
    return x == 0.0 && double.IsNegativeInfinity(1.0 / x);
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然我赞赏这种独创性,但我不确定它是最可读的*方法:) (5认同)

Han*_*ant 8

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

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

编辑:正如OP指出的那样,这在发布模式下不起作用.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;
    }
Run Code Online (Sandbox Code Playgroud)

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


Gab*_*abe 7

这是另一个黑客.它利用了一个事实,即Equals在一个struct遗嘱上做一个按位比较,而不是调用Equals它的成员:

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

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