最初我认为Math.Sign
是正确的方法,但在运行测试后,似乎它会同时处理-0.0
和+0.0
。
答案 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%的性能提升但是。在发布模式下编译并从命令行运行后,没有显着差异。
我无法使用不安全的代码来提高性能,但这可能是由于我的经验不足。