4 Stimmen

Umwandlung eines Double in eine Integer-Zahl für GetHashCode in Delphi

Delphi 2009 fügte die Funktion GetHashCode zu TObject hinzu. GetHashCode gibt einen Integer zurück, der für das Hashing in TDictionary verwendet wird.

Wenn Sie möchten, dass ein Objekt gut in TDictionary funktioniert, müssen Sie GetHashCode entsprechend überschreiben, so dass im Allgemeinen verschiedene Objekte verschiedene Integer-Hash-Codes zurückgeben.

Aber was machen Sie mit Objekten, die Doppelfelder enthalten? Wie verwandeln Sie diese doppelten Werte in eine ganze Zahl für GetHashCode?

In Java wird dies normalerweise mit einer Methode wie Double.doubleToLongBits oder Float.floatToIntBits gemacht. Die Dokumentation der letztgenannten Methode beschreibt sie wie folgt: "Gibt eine Darstellung des angegebenen Fließkommawertes gemäß dem IEEE 754 Fließkomma-Bit-Layout "Single Format" zurück." Dies beinhaltet einige bitweise Operationen mit unterschiedlichen Masken für die verschiedenen Bits einer Fließkommazahl.

Gibt es eine Funktion, die dies in Delphi ermöglicht?

6voto

Kcats Punkte 884

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;

Dabei werden alle Bits des Double-Wertes berücksichtigt.

(Kommentare funktionieren nicht gut mit Code)

2voto

Toon Krijthe Punkte 51819

Wenn Sie ein Double auf eine ganze Zahl abbilden wollen, können Sie einen Variantensatz 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;

Beachten Sie, dass dies eine bitweise Kopie ohne Interpretation der Werte ist.

Ein weiterer (etwas schmutziger) Trick ist die Verwendung absoluter 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;

1voto

Mason Wheeler Punkte 79858

Es besteht eigentlich keine Notwendigkeit, so etwas zu tun, denn der Standardwert von GetHashCode gibt bereits eine Zahl zurück, die für jedes Objekt garantiert eindeutig ist: die Speicheradresse des Objekts. Außerdem 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 Double mit dem Wert 3,5 enthält, und Sie verschlüsseln es und legen es in ein Wörterbuch, und Sie erhalten einen Hash-Code von 12345678. Sie haben auch etwas anderes, das einen Verweis darauf enthält, und das Double-Feld wird geändert und hat jetzt den Wert 5,21. Wenn Sie das nächste Mal versuchen, den Hash-Wert zu berechnen, lautet der Hash-Code nun 23456789, und die Suche wird fehlschlagen.

Wenn Sie nicht garantieren können, dass dies nie passiert, und Sie einen wirklich guten Grund haben, die Speicheradresse nicht zu verwenden, ist es am besten, GetHashCode so zu lassen, wie es ist. (Wenn es nicht kaputt ist, sollte man es nicht reparieren.)

1voto

Uwe Raabe Punkte 41879

Ich denke, dass die Java-Sache in Delphi so implementiert werden kann:

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 ist, die Genauigkeit des Double-Werts zu verringern, um die binäre Größe eines Integer-Werts zu erreichen (Sizeof(Single) = Sizeof(Integer)). Wenn Ihre Werte in einfacher Genauigkeit ohne Kollisionen dargestellt werden können, ergibt dies einen guten Hash-Wert.

Edit: Da der Typecast in meinem D2009 nicht kompiliert werden kann, habe ich die Lösung mit dem Variant Record angepasst.

1voto

pani Punkte 1035

Verwenden Sie CRC32 für die Double-Daten, weil xoder 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.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X