Frage

Delphi 2009 fügte die Gethashcode -Funktion zum Tobject hinzu. Gethashcode gibt eine Ganzzahl zurück, die zum Hashing in tdictionary verwendet wird.

Wenn Sie möchten, dass ein Objekt in TDictionary gut funktioniert, müssen Sie Gethashcode angemessen überschreiben, sodass unterschiedliche Objekte im Allgemeinen unterschiedliche Integer -Hash -Codes zurückgeben.

Aber was tun Sie für Objekte, die Doppelfelder enthalten? Wie verwandeln Sie diese doppelten Werte für Gethashcode in eine ganze Zahlen?

Die Art und Weise, wie es normalerweise in Java gemacht wird, besteht darin, eine Methode wie Double.Doubletolongbits oder Float.Floattointbits zu verwenden. Letzteres hat eine Dokumentation, die es wie folgt beschreibt: "Gibt eine Darstellung des angegebenen Schwimmpunktwerts gemäß dem IEEE 754 Floating-Punkt" Single-Format "Bit-Layout" zurück. Dies beinhaltet einige bitweise Operationen mit unterschiedlichen Masken für die verschiedenen Bits eines schwebenden Punktwerts.

Gibt es eine Funktion, die dies in Delphi macht?

War es hilfreich?

Lösung

Ich würde die folgende Verbesserung gegenüber dem Gamecat -Code vorschlagen:

type
  TVarRec = record
    case Integer of
      0: ( FInt1, FInt2 : Integer; )
      1: ( FDouble : Double; )
  end;

function Convert(const ADouble: Double): Integer;
var
  arec : TVarRec;
begin
  arec.FDouble := ADouble;
  Result := arec.FInt1 xor arec.FInt2;
end;

Dies berücksichtigt alle Bits des Doppelwerts.

(Kommentare funktionieren nicht gut mit Code)

Andere Tipps

Wenn Sie ein Doppel einer Ganzzahl zuordnen möchten, können Sie einen Variantendatensatz verwenden:

type
  TVarRec = record
    case Integer of
      0: ( FInt : Integer; )
      1: ( FDouble : Double; )
  end;


function Convert(const ADouble: Double): Integer;
var
  arec : TVarRec;
begin
  arec.FDouble := ADouble;
  Result := arec.FInt;
end;

Achten Sie darauf, dass dies eine bitweise Kopie ohne Interpretation der Werte macht.

Ein anderer (Art von schmutziger Trick, verwendet absolute Variablen:

function Convert(const ADouble: Double): Integer;
var
  tempDouble : Double;
  tempInt    : Integer absolute tempDouble; // tempInt is at the same memory position as tempDouble.
begin
  tempDouble := ADouble;
  Result := tempInt;
end;

Es ist wirklich nicht erforderlich, so etwas zu tun, da der Standardwert von Gethashcode bereits eine Nummer zurückgibt, die für jedes Objekt garantiert eindeutig ist: die Speicheradresse des Objekts. Darüber hinaus wird sich der Standard -Hash -Wert nicht ändern, wenn Sie die Daten ändern, die Ihr Objekt enthält.

Nehmen wir an, Sie haben ein Objekt, das ein Doppel mit einem Wert von 3,5 enthält, und Sie haben es in ein Wörterbuch eingerichtet, und Sie erhalten einen Hash -Code von 12345678. Sie haben auch etwas anderes, das einen Verweis auf sie hält und dieses Doppel Field wird geändert und jetzt hat es einen Wert von 5,21. Wenn Sie das nächste Mal versuchen, seinen Hash -Wert zu berechnen, ist Ihr Hash -Code jetzt 23456789 und Ihre Suche wird fehlschlagen.

Wenn Sie nicht garantieren können, dass dies niemals passieren wird und Sie einen wirklich guten Grund haben, die Speicheradresse nicht zu verwenden, ist es am besten, Gethashcode so wie sie ist. (Wenn es nicht kaputt ist, reparieren Sie es nicht.)

Ich denke, das Java -Ding kann in Delphi wie folgt implementiert werden:

type
  TVarRec = record
    case Integer of
      0: ( FInt1: Integer; )
      1: ( FSingle: Single; )
  end;

function GetHashCode(Value: Double): Integer;
var
  arec: TVarRec;
begin
  arec.FSingle := Value;
  Result := arec.FInt1;
end;

Die Idee dahinter besteht darin, die Genauigkeit des Doppelwerts zu verringern, um der binären Größe einer Ganzzahl (sizeof (einzeln) = sizeof (Ganzzahl)) zu reduzieren. Wenn Ihre Werte ohne Kollision in einzelnen Präzision dargestellt werden können, gibt dies einen guten Hash -Wert.

Bearbeiten: Da die Typecast in meinem D2009 nicht kompiliert wird, habe ich die Variante -Datensatzlösung angepasst.

Verwenden Sie CRC32 für die Doppeldaten, weil xor ist böse.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TVarRec = record
    case Integer of
      0: ( FInt1, FInt2 : Integer; );
      1: ( FDouble : Double; );
  end;

function Convert(const ADouble: Double): Integer;
var
  arec : TVarRec;
begin
  arec.FDouble := ADouble;
  Result := arec.FInt1 xor arec.FInt2;
end;

var
  FDoubleVar1, FDoubleVar2: TVarRec;
  HashCode1, HashCode2: Integer;
begin
  // Make a Double
  FDoubleVar1.FInt1 := $DEADC0DE;
  FDoubleVar1.FInt2 := $0C0DEF00;

  // Make another Double
  FDoubleVar2.FInt1 := $0C0DEF00;
  FDoubleVar2.FInt2 := $DEADC0DE;

  WriteLn('1rst Double   : ', FDoubleVar1.FDouble);
  WriteLn('2nd Double    : ', FDoubleVar2.FDouble);

  HashCode1 := Convert(FDoubleVar1.FDouble);
  HashCode2 := Convert(FDoubleVar2.FDouble);

  WriteLn('1rst HashCode : ', HashCode1);
  WriteLn('2nd HashCode  : ', HashCode2);

  if HashCode1 = HashCode2 then
  begin
    WriteLn('Warning: Same HashCode!');
  end;
  ReadLn;
end.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top