10

我有一个大字典,其中的键是十进制,但是 System.Decimal 的 GetHashCode() 非常糟糕。为了证明我的猜测,我运行了一个包含 100.000 个相邻小数的 for 循环并检查了分布。100.000 个不同的十进制数字只使用了 2 个(两个!!!)不同的哈希码。

十进制表示为 16 个字节。就像吉德一样!但是 Guid 的 GetHashCode() 分布非常好。如何尽可能便宜地将小数转换为 C# 中的 Guid? 不安全的代码是可以的!


编辑:要求测试,所以这里是代码:

decimal d = 96000000000000000000m;
Dictionary<int, int> hashcount = new Dictionary<int, int>();
int length = 100000;
for (int i = 0; i < length; i++)
{
    int hashcode = d.GetHashCode();
    int n;
    if (hashcount.TryGetValue(hashcode, out n))
    {
        hashcount[hashcode] = n + 1;
    }
    else
    {
        hashcount.Add(hashcode, 1);
    }
    d++;
}

Console.WriteLine(hashcount.Count);

这会打印 7。我不记得给我 2 的起始小数。

4

4 回答 4

23

极其骇人听闻的解决方案(但可能是最快的)

public static class Utils
{
    [StructLayout(LayoutKind.Explicit)]
    struct DecimalGuidConverter
    {
        [FieldOffset(0)]
        public decimal Decimal;
        [FieldOffset(0)]
        public Guid Guid;
    }

    private static DecimalGuidConverter _converter;
    public static Guid DecimalToGuid(decimal dec)
    {
        _converter.Decimal = dec;
        return _converter.Guid;
    }
    public static decimal GuidToDecimal(Guid guid)
    {
        _converter.Guid = guid;
        return _converter.Decimal;
    }
}

// Prints 000e0000-0000-0000-8324-6ae7b91d0100
Console.WriteLine(Utils.DecimalToGuid((decimal) Math.PI));

// Prints 00000000-0000-0000-1821-000000000000
Console.WriteLine(Utils.DecimalToGuid(8472m));

// Prints 8472
Console.WriteLine(Utils.GuidToDecimal(Guid.Parse("00000000-0000-0000-1821-000000000000")));
于 2010-08-25T07:54:01.273 回答
5

如果您只是想获得不同的哈希算法,则无需转换为 Guid。像这样的东西:

public int GetDecimalHashCode(decimal value)
{
    int[] bits = decimal.GetBits(value);
    int hash = 17;
    foreach (int x in bits)
    {
        hash = hash * 31 + x;
    }
    return hash;
}

(如果需要,显然可以替换为不同的算法。)

诚然,这仍然涉及创建一个数组,这并不理想。如果您真的想创建一个 Guid,您可以使用上面的代码来获取位,然后使用长Guid构造函数从数组中传递适当的值。

不过,我有点怀疑decimal哈希码是如此糟糕。你有一些示例代码吗?

于 2010-08-25T07:59:11.483 回答
0

将您的十进制值转换为字节数组,然后从中创建一个 guid:

public static byte[] DecimalToByteArray (decimal src) 
{
    using (MemoryStream stream = new MemoryStream()) 
    {
        using (BinaryWriter writer = new BinaryWriter(stream))
        {
            writer.Write(src);
            return stream.ToArray();
        }
    }
}

Decimal myDecimal = 1234.5678M;
Guid guid = new Guid(DecimalToByteArray(myDecimal));
于 2010-08-25T07:50:40.560 回答
0

GUID 的分布很好,因为它是唯一的......

用于此的数字范围是多少?的默认GetHashcode()实现Decimal可能只考虑一定范围的值。

于 2010-08-25T07:51:05.083 回答