2017-05-07 63 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

非常感謝。輝煌的調查。 –