代码之家  ›  专栏  ›  技术社区  ›  MB.

Delphi中的GetHashCode将double转换为整数

  •  3
  • MB.  · 技术社区  · 15 年前

    Delphi2009向tobject添加了gethashcode函数。GetHashCode返回一个整数,用于在TDictionary中散列。

    如果希望一个对象在TDictionary中工作良好,则需要适当地重写GetHashCode,这样,一般来说,不同的对象返回不同的整数哈希代码。

    但是对于包含双字段的对象,您要做什么呢?如何将这些双精度值转换为gethashcode的整数?

    在Java中通常使用的方法是使用一种方法,比如Douple。后者的文档描述如下:“根据IEEE754浮点“单格式”位布局返回指定浮点值的表示形式。”这涉及对浮点值的不同位使用不同掩码的某些位操作。

    Delphi中是否有这样的函数?

    5 回复  |  直到 15 年前
        1
  •  5
  •   Kcats    15 年前

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

    这考虑了双精度值的所有位。

    (注释不适用于代码)

        2
  •  2
  •   Toon Krijthe Paul    15 年前

    如果要将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;
    
        3
  •  0
  •   Mason Wheeler    15 年前

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

    假设您有一个包含值为3.5的double的对象,您将其散列并放入字典中,得到的散列代码是12345678。你还有其他的东西,它有一个引用,这个双字段被改变了,现在它的值是5.21。下次尝试计算散列值时,散列代码现在为23456789,查找将失败。

    除非你能保证永远不会发生这种情况,而且你有一个很好的理由不使用内存地址,否则你最好的选择就是保持gethashcode不变。(如果没有坏,就不要修。)

        4
  •  0
  •   Uwe Raabe    15 年前

    我猜想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;
    

    背后的想法是降低双精度值的精度,以匹配整数的二进制大小(size of(single)=size of(integer))。如果您的值可以在不发生冲突的情况下以单精度表示,这将提供一个好的哈希值。

    编辑:由于类型转换无法在我的D2009中编译,所以我修改了变体记录解决方案。

        5
  •  0
  •   pani    15 年前

    对双数据使用CRC32,因为 异或 是邪恶的。

    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.