Pregunta

Delphi 2009 añadió la función GetHashCode a TObject. GetHashCode devuelve un entero que se utiliza para hashing en TDictionary.

Si desea que un objeto para trabajar bien en TDictionary, es necesario reemplazar GetHashCode apropiadamente de manera que, en general, diferentes objetos devuelven diferentes códigos número entero de patata.

Pero, ¿qué hacer para objetos que contienen campos de dobles? ¿Cómo convertir esos valores en una doble enteros para GetHashCode?

La forma en que se realiza generalmente en Java, por ejemplo, es el uso de un método como Double.doubleToLongBits o Float.floatToIntBits. Este último tiene la documentación que lo describe de la siguiente manera: "Devuelve una representación del valor de punto flotante especificado de acuerdo con la norma IEEE 754 de punto flotante 'formato único' diseño poco." Esto implica algunas operaciones bit a bit con diferentes máscaras para los diferentes bits de un valor de punto flotante.

¿Hay una función que hace esto en Delphi?

¿Fue útil?

Solución

Me gustaría sugerir la siguiente mejora sobre el 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;

Esto toma en cuenta todos los bits del valor doble.

(los comentarios no funcionan bien con el código)

Otros consejos

Si desea asignar un doble a un número entero, se puede utilizar un registro variante:

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;

Tenga en cuenta que esto hace una copia bit a bit sin interpretación de los valores.

Otro (tipo de truco sucio, es el uso de variables absolutas:

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;

En realidad no hay necesidad de hacer algo como esto, debido a que el valor por defecto de GetHashCode ya se devuelve un número que se garantiza que sea único para cada objeto: dirección de memoria del objeto. Por otra parte, el valor hash por defecto no va a cambiar si cambia los datos de su objeto contiene.

Digamos que usted tiene un objeto que contiene un doble con un valor de 3,5, y hash y lo puso en un diccionario, y se obtiene un código hash de 12345678. También tiene algo más que sostiene una referencia a él, y ese campo se cambia doble y ahora que tiene un valor de 5,21. La próxima vez que intenta calcular su valor hash, su código hash es ahora 23.456.789, y su consulta de fallará.

A menos que usted puede garantizar que esto nunca sucederá, y tiene una muy buena razón para no usar la dirección de memoria, lo mejor es que acaba de salir de GetHashCode tal como es. (Si no está roto, no lo arregles.)

Creo que lo Java puede ser implementado en Delphi como esto:

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;

La idea detrás es reducir la precisión del valor doble para que coincida con el tamaño de un entero binario (sizeof (Individual) = sizeof (entero)). Si sus valores pueden ser representados en la precisión individual sin colisión, esto le dará un buen valor hash.

Editar:. A medida que el encasillado no se compilará en mi D2009, me he adaptado la solución registro variante

El uso de los datos CRC32 doble porque xor es el 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.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top