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

后端 未结 3 938
情歌与酒
情歌与酒 2020-12-18 18:13

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

相关标签:
3条回答
  • 2020-12-18 18:34

    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
    
    0 讨论(0)
  • 2020-12-18 18:34

    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

    0 讨论(0)
  • 2020-12-18 18:43

    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".

    0 讨论(0)
提交回复
热议问题