Question

Delphi 2009 a ajouté la fonction GetHashCode à TObject. GetHashCode retourne un entier qui est utilisée pour le hachage en TDictionary.

Si vous voulez un objet de bien travailler dans TDictionary, vous devez passer outre GetHashCode appropriée telle que, dans les objets généraux, différents renvoient différents codes de hachage entier.

Mais qu'est-ce que vous faites pour les objets contenant des doubles champs? Comment transformer ces doubles valeurs dans un entiers pour GetHashCode?

La façon dont il est généralement fait en Java, par exemple, est d'utiliser une méthode comme Double.doubleToLongBits ou Float.floatToIntBits. Ce dernier dispose d'une documentation qui décrit comme suit: « Retourne une représentation de la valeur à virgule flottante spécifiée selon la norme IEEE 754 à virgule flottante « format unique » mise en page peu. » Ceci implique des opérations au niveau du bit avec des masques différents pour les différents bits d'une valeur à virgule flottante.

Y at-il une fonction qui fait cela dans Delphi?

Était-ce utile?

La solution

Je vous suggère l'amélioration suivante sur le code Gamecat:

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;

Il prend en compte tous les bits de la valeur double.

(commentaires bien avec le code fonctionne pas)

Autres conseils

Si vous voulez mapper un double à un nombre entier, vous pouvez utiliser un enregistrement variant:

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;

Attention que cela ne copie sans interprétation des bitwise valeurs.

Une autre (genre de truc sale, utilise des variables absolues:

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;

Il n'y a vraiment pas besoin de faire quelque chose comme ça, parce que la valeur par défaut de GetHashCode retourne déjà un nombre qui est garanti d'être unique pour chaque objet: l'adresse mémoire de l'objet. En outre, la valeur de hachage par défaut ne va pas changer si vous modifiez les données de votre objet contient.

Disons que vous avez un objet qui contient une double avec une valeur de 3,5, et vous hash et le mettre dans un dictionnaire, et vous obtenez un code de hachage de 12345678. Vous avez aussi quelque chose tenant encore une référence à elle, et ce champ Double est changée et maintenant il a une valeur de 5,21. La prochaine fois que vous essayez de calculer sa valeur de hachage, votre code de hachage est maintenant 23.456.789, et votre recherche échouera.

Sauf si vous pouvez garantir que cela ne se produira jamais, et vous avez une très bonne raison de ne pas utiliser l'adresse mémoire, votre meilleur pari est juste laisser GetHashCode comme il est. (Si ce n'est pas cassé, ne le répare pas.)

Je suppose que la chose Java peut être mis en œuvre dans Delphi comme ceci:

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;

L'idée est derrière de réduire la précision de la valeur double pour correspondre à la taille binaire d'un entier (Sizeof (Simple) = Sizeof (entier)). Si vos valeurs peuvent être représentées en simple précision sans collision, cela donnera une bonne valeur de hachage.

Modifier. Comme le transtypage ne compilera pas dans mon D2009, je me suis adapté la solution d'enregistrement variant

Utilisez CRC32 des données doubles parce que XOR est le mal.

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.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top