Преобразование System.Decimal в System.Guid
У меня есть большой словарь, где ключ десятичный, но 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 ответа
ЧРЕЗВЫЧАЙНО РЕШЕНИЕ (но, возможно, самое быстрое)
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")));
Если вы просто пытаетесь получить другой алгоритм хеширования, нет необходимости конвертировать в 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
хотя хеш-код такой плохой. У вас есть пример кода для этого?
Преобразуйте ваше десятичное значение в байтовый массив, а затем создайте из него 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));
Распределение GUID хорошо, так как оно должно быть уникальным...
Какой диапазон чисел используется для этого? По умолчанию GetHashcode()
реализация для Decimal
может принимать во внимание только определенный диапазон значений.