Can anyone explain this strange behavior with signed floats in C#?

前端 未结 11 1988
别跟我提以往
别跟我提以往 2020-12-07 07:17

Here is the example with comments:

class Program
{
    // first version of structure
    public struct D1
    {
        public double d;
        public int f         


        
相关标签:
11条回答
  • 2020-12-07 07:36

    It must be related to a bit by bit comparison, since 0.0 should differ from -0.0 only by the signal bit.

    0 讨论(0)
  • 2020-12-07 07:39

    The bug is in the following two lines of System.ValueType: (I stepped into the reference source)

    if (CanCompareBits(this)) 
        return FastEqualsCheck(thisObj, obj);
    

    (Both methods are [MethodImpl(MethodImplOptions.InternalCall)])

    When all of the fields are 8 bytes wide, CanCompareBits mistakenly returns true, resulting in a bitwise comparison of two different, but semantically identical, values.

    When at least one field is not 8 bytes wide, CanCompareBits returns false, and the code proceeds to use reflection to loop over the fields and call Equals for each value, which correctly treats -0.0 as equal to 0.0.

    Here is the source for CanCompareBits from SSCLI:

    FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
    {
        WRAPPER_CONTRACT;
        STATIC_CONTRACT_SO_TOLERANT;
    
        _ASSERTE(obj != NULL);
        MethodTable* mt = obj->GetMethodTable();
        FC_RETURN_BOOL(!mt->ContainsPointers() && !mt->IsNotTightlyPacked());
    }
    FCIMPLEND
    
    0 讨论(0)
  • 2020-12-07 07:39

    Simpler test case:

    Console.WriteLine("Good: " + new Good().Equals(new Good { d = -.0 }));
    Console.WriteLine("Bad: " + new Bad().Equals(new Bad { d = -.0 }));
    
    public struct Good {
        public double d;
        public int f;
    }
    
    public struct Bad {
        public double d;
    }
    

    EDIT: The bug also happens with floats, but only happens if the fields in the struct add up to a multiple of 8 bytes.

    0 讨论(0)
  • 2020-12-07 07:41

    If you make D2 like this

    public struct D2
    {
        public double d;
        public double f;
        public string s;
    }
    

    it's true.

    if you make it like this

    public struct D2
    {
        public double d;
        public double f;
        public double u;
    }
    

    It's still false.

    it seems like it's false if the struct only holds doubles.

    0 讨论(0)
  • 2020-12-07 07:42

    It must be zero related, since changing the line

    d.d = -0.0

    to:

    d.d = 0.0

    results in the comparison being true...

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

    Vilx's conjecture is correct. What "CanCompareBits" does is checks to see whether the value type in question is "tightly packed" in memory. A tightly packed struct is compared by simply comparing the binary bits that make up the structure; a loosely packed structure is compared by calling Equals on all the members.

    This explains SLaks' observation that it repros with structs that are all doubles; such structs are always tightly packed.

    Unfortunately as we've seen here, that introduces a semantic difference because bitwise comparison of doubles and Equals comparison of doubles gives different results.

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