2017-05-07 78 views
2

我有一个代码:同样的结构有不同的hashCode

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; 
     } 
    } 

正如你可以看到我使用struct和我说怪异的行为。 如果我有这样的输入数据:

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?

+0

是的,但我认为具有相同字段的结构应该是相等的,但它们不是。 '调试。写入(coef_list [0] .a.Equals(coef_list [1] .a)); Debug.WriteLine(coef_list [0] .b.Equals(coef_list [1] .b)); Debug.WriteLine(coef_list [0] .c.Equals(coef_list [1] .c)); 的Debug.WriteLine(coef_list [0] .Equals(coef_list [1]));' 显示 '真 真正 真正 FALSE' –

+0

他们是不相等的,你的数学给你一个舍入误差,所以' - 0,25'可能不是。当输出a,b和c值时,使用'.ToString(“G17”)' –

+0

偷看字节,看起来可能是某种下溢,因为它们不完全相同字节,但给出理论上相等的值。在第一种情况下,第一个组件的“b”正好为零:“0 0000000000000000”,而在第二种情况下,它略有不同:“0 0000000000000080”。 –

回答

2

基本上你会得到一个负的零值。然而,结构的运行时默认GetHashCode似乎只是将底层字节组合起来,而不是调用该字段的GetHashCode。这里是你所看到的简化版本:采用双

:登录:0(+)
指数:

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; 
} 

输出0xFFFFFC02(-1022 )
尾数:0x0000000000000

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

等于:真,Hashcode方法:0,0

使用自定义结构

-1800534692

我已经定义了两个“正常”零和另一个是“负”零。两者的区别在于双号的符号位。除字节级别外,这两个值在所有表观方式(等于比较,GetHashCode,ToString表示)上是相等的。但是,如果将它们放入自定义结构中,则运行时的GetHashCode方法仅合并原始位,即使它们包含相同的值,也会为每个结构提供不同的哈希代码。 Equals会执行相同的操作并获得False结果。

我承认这是一个很大的难题。解决此问题的方法是确保覆盖Equals和GetHashCode以获得所需的相应平等。

其实类似的问题已经提到before显然运行时只在结构的字段都是8字节宽的时候才会这样做。

+0

非常感谢。辉煌的调查。 –