相同的结构具有不同的HashCode

时间:2017-05-07 19:30:05

标签: c# .net gethashcode

我有一个代码:

    public class Point
    {
        public int x;
        public int y;
        public Point() { x = 0; y = 0; }
        public Point(int a, int b) { x = a; y = b; }
    }
    public struct Coefficients{
        public double a;
        public double b;
        public double c;
        public Coefficients(double a, double b, double c)
        {
            this.a = a;
            this.b = b;
            this.c = c;
        }
        public static Coefficients GetFromPoints(Point point1, Point point2)
        {

            int x1 = point1.x;
            int x2 = point2.x;
            int y1 = point1.y;
            int y2 = point2.y;
            double a = y1- y2;
            double b = x2 - x1;
            double c = x1 * y2 - y1 * x2 ;
            double max = Math.Max(Math.Max(a, b), c);
            double min= Math.Min(Math.Min(a, b), c);
            double divider = Math.Abs(max)> Math.Abs(min)?max:min;
            divider = Math.Abs(divider) > 1? divider : 1;
            return new Coefficients(a/divider, b/divider, c/divider);

        }

    }
public class Solution
    {
        public int MaxPoints(Point[] points)
        {
            var coef_list = new List<Coefficients>();
            for (var x = 0; x < points.Length - 1; x++)
            {
                for (var y = x + 1; y < points.Length; y++)
                {
                    var coef = Coefficients.GetFromPoints(points[x], points[y]);
                    coef_list.Add(coef);
                }
            }
            foreach (var item in coef_list) {
                Debug.WriteLine(item.a);
                Debug.WriteLine(item.b);
                Debug.WriteLine(item.c);
                Debug.WriteLine(item.GetHashCode());
                Debug.WriteLine("---------------");
            }           
            return 0;
        }
    }

正如你所看到的,我使用了一个结构,并且我评论了奇怪的行为。 如果我有这样的输入数据:

prg.MaxPoints(new Point[] { new Point(4, -1), new Point(4, 0), new Point(4, 5) });

调试输出是:

-0,25
0
1
-450335288
---------------
-0,25
0
1
-450335288
---------------
-0,25
0
1
-450335288
---------------

但如果我改变args。为了:

prg.MaxPoints(new Point[] { new Point(4, 0),new Point(4, -1) , new Point(4, 5) });

调试是:

-0,25
0
1
1697148360
---------------
-0,25
0
1
-450335288
---------------
-0,25
0
1
-450335288
---------------

有一点可以重要的是,在第一种情况下,我们所有的“分隔符”(GetFromPoints方法)都是正的(4,24,20)在第二种情况下,其中一个是负面和其他两个是正面的( - 4,20,24)。 有人可以解释一下吗?

UPD。 当我改变了

return new Coefficients(a/divider, b/divider, c/divider);

return new Coefficients(a/divider, 0, c/divider);//anyway in all of these cases 2-nd argument is 0

这意味着0除以负数不是0?

1 个答案:

答案 0 :(得分:2)

基本上你得到负零值双倍。但是,结构的运行时默认的GetHashCode似乎盲目地组合了底层字节而不是调用字段的GetHashCode。以下是您所看到的简化版本:

public struct S
{
    public double value;

    public S(double d)
    {
        value = d;
    }
}

public static void Main(string[] args)
{           
    double d1 = 0;
    double d2 = d1 / -1;

    Console.WriteLine("using double");
    Console.WriteLine("{0} {1}", d1, d1.GetHashCode());
    Console.WriteLine(GetComponentParts(d1));
    Console.WriteLine("{0} {1}", d2, d2.GetHashCode());
    Console.WriteLine(GetComponentParts(d2));
    Console.WriteLine("Equals: {0}, Hashcode:{1}, {2}", d1.Equals(d2), d1.GetHashCode(), d2.GetHashCode());

    Console.WriteLine();
    Console.WriteLine("using a custom struct");

    var s1 = new S(d1);
    var s2 = new S(d2);
    Console.WriteLine(s1.Equals(s2));
    Console.WriteLine(new S(d1).GetHashCode());
    Console.WriteLine(new S(d2).GetHashCode());            
}

// from: https://msdn.microsoft.com/en-us/library/system.double.epsilon(v=vs.110).aspx
private static string GetComponentParts(double value)
{
    string result = String.Format("{0:R}: ", value);
    int indent = result.Length;

    // Convert the double to an 8-byte array.
    byte[] bytes = BitConverter.GetBytes(value);
    // Get the sign bit (byte 7, bit 7).
    result += String.Format("Sign: {0}\n", 
                          (bytes[7] & 0x80) == 0x80 ? "1 (-)" : "0 (+)");

    // Get the exponent (byte 6 bits 4-7 to byte 7, bits 0-6)
    int exponent = (bytes[7] & 0x07F) << 4;
    exponent = exponent | ((bytes[6] & 0xF0) >> 4);  
    int adjustment = exponent != 0 ? 1023 : 1022;
    result += String.Format("{0}Exponent: 0x{1:X4} ({1})\n", new String(' ', indent), exponent - adjustment);

    // Get the significand (bits 0-51)
    long significand = ((bytes[6] & 0x0F) << 48); 
    significand = significand | ((long) bytes[5] << 40);
    significand = significand | ((long) bytes[4] << 32);
    significand = significand | ((long) bytes[3] << 24);
    significand = significand | ((long) bytes[2] << 16);
    significand = significand | ((long) bytes[1] << 8);
    significand = significand | bytes[0];    
    result += String.Format("{0}Mantissa: 0x{1:X13}\n", new String(' ', indent), significand);    

    return result;   
}

输出:

  

使用双
  0 0
  0:符号:0(+)
     指数:0xFFFFFC02(-1022)
     尾数:0x0000000000000

     

0 0
  0:符号:1( - )
     指数:0xFFFFFC02(-1022)
     尾数:0x0000000000000

     

Equals:True,Hashcode:0,0

     

使用自定义结构
  假
  346948956个
  -1800534692

我定义了两个双重的一个是&#34; normal&#34;零和另一个是&#34;否定&#34;零。两者之间的区别在于双符号位。除字节级别外,这两个值在所有明显的方式(Equals比较,GetHashCode,ToString表示)中是相等的。然而,当它们被放入自定义结构时,运行时的GetHashCode方法只是组合原始位,即使它们包含相等的值,也为每个结构提供不同的哈希码。 Equals做同样的事情,得到一个假结果。

我承认这有点大问题。解决方案是确保您重写Equals和GetHashCode以获得所需的正确相等。

实际上已经提到类似的问题before显然运行时只在结构的字段全8字节宽时才这样做。