Initially I thought Math.Sign
would be the proper way to go but after running a test it seems that it treats -0.0
and +0.0
the same.
Here's a grotty hack way of doing it:
private static readonly long NegativeZeroBits =
BitConverter.DoubleToInt64Bits(-0.0);
public static bool IsNegativeZero(double x)
{
return BitConverter.DoubleToInt64Bits(x) == NegativeZeroBits;
}
Basically that's testing for the exact bit pattern of -0.0, but without having to hardcode it.
unsafe
code sometimes :) –
Renferd return *(((long*) &value));
–
Examinee bool IsNegative(double x){return BitConverter.DoubleToInt64Bits(x) < 0;}
. That's a function which should actually be in the standard Math
class. –
Alfonzoalford After a bit of searching I finally made it to Section 7.7.2 of the C# specification and came up with this solution.
private static bool IsNegativeZero(double x)
{
return x == 0.0 && double.IsNegativeInfinity(1.0 / x);
}
Negative zero has the sign bit set. Thus:
public static bool IsNegativeZero(double value) {
if (value != 0) return false;
int index = BitConverter.IsLittleEndian ? 7 : 0;
return BitConverter.GetBytes(value)[index] == 0x80;
}
Edit: as the OP pointed out, this doesn't work in Release mode. The x86 JIT optimizer takes the if() statement seriously and loads zero directly rather than loading value. Which is indeed more performant. But that causes the negative zero to be lost. The code needs to be de-tuned to prevent this:
public static bool IsNegativeZero(double value) {
int index = BitConverter.IsLittleEndian ? 7 : 0;
if (BitConverter.GetBytes(value)[index] != 0x80) return false;
return value == 0;
}
This is quite typical behavior for the x86 jitter btw, it doesn't handle corner cases really well when it optimizes floating point code. The x64 jitter is much better in that respect. Although there's arguably no worse corner case than giving meaning to negative zero. Be forewarned.
-0.0
will return false
. Oh and this seems to be the middle ground performance wise. –
Examinee -0.0
. –
Examinee Here's another hack. It takes advantage of the fact that Equals
on a struct
will do a bitwise comparison instead of calling Equals
on its members:
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
ValueType.Equals
not always performs a bitwise comparison. And, unfortunately, exact conditions when it chooses to do so are undocumented, and may vary between CLR versions/platforms. –
Ducks x == 0 && 1 / x < 0
More generally, you can do,
bool IsNegative(double value)
{
const ulong SignBit = 0x8000000000000000;
return ((ulong)BitConverter.DoubleToInt64Bits(value) & SignBit) == SignBit;
}
or alternatively, if you prefer,
[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;
}
The later gives an approximate 50% performance improvment in debug but. Once compiled in release mode and run from the command line there is no significant difference.
I couldn't generate a performance improvement using unsafe code either but this may be due to my inexperience.
I'm a bit baffled that after sitting here for more than 13 years (at the time of writing), this question has all these excellent (and somewhat elaborate) answers but not the most "straightforward" (IMO) one:
static bool IsNegativeZero(double d) => Double.IsNegative(d) && d == 0.0;
Note that, according to the IEEE spec, -0.0 compares equal to +0.0, so there's no need to write d == -0.0
although that will also work.
double.IsNegative()
was first introduced in .NET Core 2.1 (released in 2018), so the function didn't exist at the time the earlier answers were written. –
Lambdacism © 2022 - 2025 — McMap. All rights reserved.