DelphiのGethashCodeの整数にダブルを変換する
-
18-09-2019 - |
質問
Delphi 2009は、GethashCode関数をTobjectに追加しました。 GethashCodeは、tdictionaryのハッシュに使用される整数を返します。
オブジェクトをtdictionaryでうまく動作させたい場合は、一般に異なるオブジェクトが異なる整数ハッシュコードを返すように、GethashCodeを適切にオーバーライドする必要があります。
しかし、二重フィールドを含むオブジェクトに対して何をしますか? GethashCodeの二重値を整数にどのように変換しますか?
たとえば、Javaで通常行われる方法は、Double.Doubletolongbitsやfloat.floattointbitsなどの方法を使用することです。後者には、次のように説明するドキュメントがあります。「IEEE 754フローティングポイント「シングルフォーマット」ビットレイアウトに従って、指定された浮動小数点値の表現を返します。」これには、浮動小数点値の異なるビットに対して異なるマスクを使用したビットワイズ操作が含まれます。
Delphiでこれを行う関数はありますか?
解決
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;
これにより、二重値のすべてのビットが考慮されます。
(コメントはコードでうまく機能しません)
他のヒント
ダブルを整数にマッピングする場合は、バリアントレコードを使用できます。
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;
GethashCodeのデフォルト値は、各オブジェクトの一意であることが保証されている数値であるオブジェクトのメモリアドレスをすでに返すため、このようなことをする必要はありません。さらに、オブジェクトが含むデータを変更しても、デフォルトのハッシュ値は変更されません。
値が3.5のダブルを含むオブジェクトがあり、ハッシュして辞書に入れて、12345678のハッシュコードを取得したとしましょう。フィールドが変更され、現在5.21の値があります。次回ハッシュ値を計算しようとすると、ハッシュコードは23456789になり、ルックアップは失敗します。
これが決して起こらないことを保証できない限り、メモリアドレスを使用しないという本当に正当な理由がありますが、あなたの最善の策は、GethashCodeをそのまま残すことです。 (壊れていない場合は、修正しないでください。)
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(整数))のバイナリサイズに一致するように、二重値の精度を減らすことです。衝突なしで値を単一の精度で表現できる場合、これにより良いハッシュ値が得られます。
編集:TypeCastがD2009でコンパイルされないため、バリアントレコードソリューションを適合させました。
ダブルデータで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.