Pergunta

Delphi 2009, que acrescentou o GetHashCode função de TObject.GetHashCode retorna um número Inteiro que é usado para o hash em TDictionary.

Se você deseja um objeto para funcionar bem em TDictionary, você precisará substituir GetHashCode adequadamente de tal forma que, em geral, diferentes objetos de retorno diferentes inteiro códigos de hash.

Mas o que fazer para objectos que contenham campos duplas?Como transformar esses valores duplos em uma inteiros para GetHashCode?

A forma como é normalmente feito em Java, dizem, é a utilização de um método, como Dupla.doubleToLongBits ou Float.floatToIntBits.O último tem a documentação que descreve-a da seguinte forma:"Retorna uma representação do especificado valor de ponto flutuante de acordo com a norma IEEE 754 de ponto flutuante "único" formato de bits de layout." Isto envolve algumas operações bit a bit com máscaras diferentes para os diferentes bits de um valor de ponto flutuante.

Existe uma função que faz isso no Delphi?

Foi útil?

Solução

Eu sugeriria a seguinte melhoria em relação ao código 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;

Isso leva em consideração todos os bits do valor duplo.

(Os comentários não funcionam bem com o código)

Outras dicas

Se você deseja mapear um casal para um inteiro, você pode usar uma variante do registro:

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;

Tenha em atenção que esta faz uma cópia bit a bit sem interpretação dos valores.

Outro (tipo de truque sujo, está usando absoluta variáveis:

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;

Realmente não há necessidade de fazer algo assim, porque o valor padrão do GethashCode já retorna um número que é garantido para cada objeto: o endereço de memória do objeto. Além disso, o valor do hash padrão não vai alterar se você alterar os dados que seu objeto contém.

Digamos que você tenha um objeto que contenha um dobro com um valor de 3,5, e você o hash e o coloca em um dicionário, e você recebe um código de hash de 12345678. Você também tem algo mais segurando uma referência e esse dobro O campo é alterado e agora tem um valor de 5,21. Da próxima vez que você tentar calcular seu valor de hash, seu código de hash é agora 23456789 e sua pesquisa falhará.

A menos que você possa garantir que isso nunca acontecerá e você tem um bom motivo para não usar o endereço de memória, sua melhor aposta é deixar o GethashCode como é. (Se não estiver quebrado, não conserte.)

Eu acho que a coisa do java pode ser implementada em Delphi como esta:

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;

A idéia por trás é reduzir a precisão do valor duplo para corresponder ao tamanho binário de um número inteiro (sizeof (único) = sizeof (número inteiro)). Se seus valores puderem ser representados em uma única precisão sem colisão, isso dará um bom valor de hash.

EDIT: Como o TypeCast não compilará no meu D2009, adaptei a solução de registro variante.

Use o CRC32 nos dados duplos porque xor é mau.

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.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top