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

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

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

3条回答
  •  青春惊慌失措
    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".

提交回复
热议问题