Преобразование System.Decimal в System.Guid - PullRequest
10 голосов
/ 25 августа 2010

У меня есть большой словарь, где ключ десятичный, но GetHashCode () System.Decimal катастрофически плох.Чтобы доказать свою догадку, я запустил цикл for с 100.000 соседних десятичных знаков и проверил распределение.100 000 различных десятичных чисел использовали только 2 (два !!!) различных хеш-кода.

Десятичное число представлено в виде 16 байтов.Так же, как Guid!Но GetHashCode () дистрибутива Guid довольно хорош. Как преобразовать десятичную дробь в Guid в C # как можно дешевле? Небезопасный код в порядке!


РЕДАКТИРОВАТЬ: Испытание было запрошено, поэтому вот код:

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 ]

23 голосов
/ 25 августа 2010

ЧРЕЗВЫЧАЙНО HACKY SOLUTION (но, вероятно, самое быстрое)

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")));
5 голосов
/ 25 августа 2010

Если вы просто пытаетесь получить другой алгоритм хеширования, нет необходимости конвертировать в 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 настолько плох.У вас есть пример кода для этого?

0 голосов
/ 25 августа 2010

Распределение GUID хорошее, так как оно должно быть уникальным ...

Какой диапазон чисел используется для этого? Реализация GetHashcode() по умолчанию для Decimal может учитывать только определенный диапазон значений.

0 голосов
/ 25 августа 2010

Преобразуйте ваше десятичное значение в байтовый массив, а затем создайте из него 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));
...