德尔福在将double转换为一个整数的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(单)= SIZEOF(整数))。如果你的值可以在单精度无碰撞来表示,这将提供一个良好的哈希值。
编辑:由于类型转换不会在我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.