Ben Aston
Ben Aston

Reputation: 55759

Is minus zero (-0) equivalent to zero (0) in C#

Is minus zero (-0) equivalent to zero (0) in C#?

Upvotes: 30

Views: 16472

Answers (3)

Kobi
Kobi

Reputation: 138097

For Decimals, there are at least 4 types of zeros:

Decimal zero = Decimal.Zero;
Decimal negativeZero1 = new Decimal(0, 0, 0, true, 0);
Decimal negativeZero2 = -0.0m;
Decimal negativeZero3 = Decimal.Negate(Decimal.Zero);

While all are equal and printed out as "0", they have different bit representation:

zero:          {0x00000000 00000000 00000000 00000000 }
negativeZero1: {0x00000000 00000000 00000000 80000000 }
negativeZero2: {0x00000000 00000000 00000000 80010000 }
negativeZero3: {0x00000000 00000000 00000000 80000000 }

Source: Decimal Negative Zero Representation

Upvotes: 13

Matt Tsōnto
Matt Tsōnto

Reputation: 1701

It sounds like you're looking for the edge cases where they aren't interchangeable, so here are some examples.

object.Equals on structs

> struct SomeStruct { public double X; }
> var a = new SomeStruct { X = 0d };
> var b = new SomeStruct { X = -0d };
> a.Equals(b)
false
>

Inversion

> 1/0d
∞
> 1/-0d
-∞
>

Explicit byte conversion

Any type of explicit byte-wise or bit-wise deconstruction, of course, or type punning. These examples are from a PC:

> BitConverter.GetBytes(0d)
byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }
> BitConverter.GetBytes(-0d)
byte[8] { 0, 0, 0, 0, 0, 0, 0, 128 }
> 

Math.Sign

Despite what you might expect, Math.Sign does not distinguish between negative and positive zero. It only tells you whether a number is equal to, greater than, or less than 0.

> Math.Sign(-0f)
0 

Math.Min and Math.Max

A few arithmetic operations have edge cases for -0. One interesting one is Math.Min (and equivalently for max), which is that when comparing signed zeros it returns the second one. So:

> BitConverter.GetBytes(Math.Min(0.0, -0.0))
byte[8] { 0, 0, 0, 0, 0, 0, 0, 128 }
> BitConverter.GetBytes(Math.Min(-0.0, 0.0))
byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }
> 

A note on irregular decimal representations

The decimal type doesn't have a negative 0 per se, but it does have multiple binary representations for zero:

> new decimal(new int[4] { 0, 0, 0, 0 })
0
> new decimal(new int[4] { 0, 0, 0, -2147483648 })
0
> 

The second example there could be considered a negative zero because it's bitwise-identical to the regular zero except with the negation bit set. But as far as the formatter's concerned it's just a zero. There are, in fact dozens of zero representations for decimal, for different decimal point shifts, all of which are arithmetically equivalent and display as 0:

> new decimal(new int[4] { 0, 0, 0, 131072 })
0.00
> new decimal(new int[4] { 0, 0, 0, 1835008 })
0.0000000000000000000000000000
> new decimal(new int[4] { 0, 0, 0, 65536 })
0.0

That said, you'll only be able to distinguish them from each other by binary-comparison or binary-conversion means. From just experimentation, it seems that the struct trick above doesn't work on them. Math.Min returns whichever zero was given second.

Bonus: Subnormal numbers

Certain patterns of bits in float and double types represent subnormal (aka denormal) values. I won't go into what they are—see the link instead—but the important thing to know is that the CLI spec explicitly declares their operations as implementation-specific. I don't know that there are platforms that treat them as 0, but there could be. On the other hand, The C# Programming Language says that they're "considered valid non-zero values".

Upvotes: 16

Lucero
Lucero

Reputation: 60236

For integers, there is no binary representation that makes a difference between 0 and -0, so they are by definition equal.

For IEEE floating-point numbers, there is a distinction of negative and positive zero. I made some tests (CLR of .NET Framework 2.0, C# 3) and it seems that they are considered equal, which is actually the behavior expected according to the IEEE 754 standard.

Here's my test code to show that:

    double minusOne = -1.0;
    double positiveZero = 0.0;
    double negativeZero = minusOne*positiveZero;
    Console.WriteLine("{0} == {1} -> {2}", positiveZero, negativeZero, positiveZero == negativeZero);
    Console.WriteLine("Binary representation is equal: {0}", BitConverter.DoubleToInt64Bits(positiveZero) == BitConverter.DoubleToInt64Bits(negativeZero));

Returns:

0 == 0 -> True
Binary representation is equal: False

Upvotes: 37

Related Questions