4

Delphi 2009 では、GetHashCode 関数が TObject に追加されました。GetHashCode は、TDictionary でのハッシュに使用される整数を返します。

オブジェクトを TDictionary で適切に動作させるには、GetHashCode を適切にオーバーライドして、一般に、異なるオブジェクトが異なる整数ハッシュ コードを返すようにする必要があります。

しかし、double フィールドを含むオブジェクトに対してはどうすればよいでしょうか? これらの double 値を GetHashCode の整​​数に変換するにはどうすればよいでしょうか?

たとえば、Java で通常行われている方法は、Double.doubleToLongBits や Float.floatToIntBits などのメソッドを使用することです。後者には、次のように説明するドキュメントがあります。これには、浮動小数点値のさまざまなビットに対してさまざまなマスクを使用したビット演算が含まれます。

Delphiでこれを行う関数はありますか?

4

5 に答える 5

6

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値のすべてのビットを考慮に入れます。

(コメントはコードではうまく機能しません)

于 2009-08-17T15:26:48.537 に答える
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;

これは、値を解釈せずにビット単位のコピーを行うことに注意してください。

もう1つの(一種の汚いトリックは、絶対変数を使用することです:

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;
于 2009-08-17T14:22:12.010 に答える
1

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;

背後にある考え方は、整数のバイナリ サイズ (Sizeof(Single) = Sizeof(Integer)) に一致するように Double 値の精度を下げることです。値が衝突せずに単精度で表現できる場合、これにより適切なハッシュ値が得られます。

編集: 私の D2009 では型キャストがコンパイルされないため、バリアント レコード ソリューションを採用しました。

于 2009-08-17T15:39:23.353 に答える
1

GetHashCode の既定値は、オブジェクトごとに一意であることが保証されている数値 (オブジェクトのメモリ アドレス) を既に返しているため、このようなことを行う必要はまったくありません。さらに、オブジェクトに含まれるデータを変更しても、デフォルトのハッシュ値は変更されません。

値が 3.5 の Double を含むオブジェクトがあり、それをハッシュして辞書に入れ、ハッシュ コード 12345678 を取得するとします。また、それへの参照を保持している何か他のものもあり、その Doubleフィールドが変更され、値が 5.21 になりました。次にそのハッシュ値を計算しようとすると、ハッシュ コードは 23456789 になり、ルックアップは失敗します。

これが決して起こらないことを保証できず、メモリ アドレスを使用しない十分な理由がない限り、最善の策は GetHashCode をそのままにしておくことです。(壊れていない場合は、修正しないでください。)

于 2009-08-17T15:31:28.747 に答える
1

xorは悪であるため、Double データには 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.
于 2009-08-17T20:08:41.597 に答える