在Delphi中将GetHashCode的Double转换为Integer

时间:2009-08-17 14:15:22

标签: delphi hash double gethashcode

Delphi 2009将GetHashCode函数添加到TObject。 GetHashCode返回一个Integer,用于在TDictionary中进行散列。

如果你想让一个对象在TDictionary中运行良好,你需要适当地重写GetHashCode,这样一般来说,不同的对象会返回不同的整数哈希码。

但是你对包含双字段的对象做了什么?如何将这些double值转换为GetHashCode的整数?

通常用Java完成的方法是使用Double.doubleToLongBits或Float.floatToIntBits之类的方法。后者的文档描述如下:“根据IEEE 754浮点”单格式“位布局返回指定浮点值的表示。”这涉及对浮点值的不同位使用不同掩码的一些按位运算。

Delphi中有没有这样做的功能?

5 个答案:

答案 0 :(得分:5)

我建议对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;

这会考虑Double值的所有位。

(评论不适用于代码)

答案 1 :(得分:2)

如果要将double映射到整数,可以使用变体记录:

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;

请注意,如果没有对值的解释,这会执行按位复制。

另一种(一种肮脏的技巧,是使用绝对变量:

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;

答案 2 :(得分:0)

实际上没有必要做这样的事情,因为GetHashCode的默认值已经返回了一个对于每个对象保证唯一的数字:对象的内存地址。此外,如果更改对象包含的数据,则默认哈希值不会更改。

假设您有一个包含值为3.5的Double的对象,并将其哈希并将其放入字典中,并获得一个哈希代码12345678.您还有其他内容包含对它的引用,并且Double字段被更改,现在它的值为5.21。下次尝试计算其哈希值时,您的哈希码现在为23456789,您的查找将失败。

除非您能保证永远不会发生这种情况,并且您有充分的理由不使用内存地址,否则最好的办法就是将GetHashCode保留原样。 (如果没有破坏,请不要修理它。)

答案 3 :(得分:0)

我想Java可以在Delphi中实现,如下所示:

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;

背后的想法是降低Double值的精度以匹配Integer的二进制大小(Sizeof(Single)= Sizeof(Integer))。如果您的值可以在没有冲突的情况下以单精度表示,则这将提供良好的哈希值。

编辑:由于类型转换不能在我的D2009中编译,我改编了变体记录解决方案。

答案 4 :(得分:0)

对Double数据使用CRC32,因为 xor 是邪恶的。

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.