如何将System.Int64表示为词法可排序字符串?

时间:2014-10-04 06:20:08

标签: c# sorting

我想将(64位)整数转换为词法可排序的字符串(即,我可以使用String.Compare对结果字符串进行排序,并获得与直接比较原始值相同的顺序)。 Jon Skeet为双打here提供了实施。我想对 Int64 (也是Int32,浮点数,日期时间,时间跨度,但我确信我可以从Int64到达那里)做同样的事情。

如何将C#中的System.Int64表示为词法可排序字符串?

我喜欢链接的实现使用ASCII字符(它是十六进制编码的),因此,如果不是严格的人类可读的话,它是模糊的人类可呈现的。

我正在寻找适用于Int64的所有有效正值和负值的方法。

这是一个适用于Jon Skeet实施“EncodeDouble”的测试,以及每种方法的实现。我的实现与链接的问题没有变化,但提供了完整性。

    [TestMethod]
    public void LexicallySortRandomDoubles()
    {
        var r = new Random(15245);

        for (int i = 0; i < 10000; ++i)
        {
            var bytes = new byte[16];
            r.NextBytes(bytes);
            var a = BitConverter.ToDouble(bytes, 0);
            var b = BitConverter.ToDouble(bytes, 8);

            // don't sort equal values, or nans.
            if (double.IsInfinity(a) || double.IsNaN(a) || double.IsInfinity(b) || double.IsNaN(b)) continue;

            var c = LexicallySortableValues.EncodeDouble(a);
            var d = LexicallySortableValues.EncodeDouble(b);

            // Comparison works
            Assert.IsTrue(
                a < b == System.String.Compare(c, d, System.StringComparison.InvariantCultureIgnoreCase) < 0,
                string.Format("i={4}, a = {0}, b = {1}, c = {2}, d = {3}", a, b, c, d, i));
        }
    }

    public static ulong DoubleToSortableULong(double d)
    {
        long ieee = System.BitConverter.DoubleToInt64Bits(d);
        const ulong widezero = 0;
        return ((ieee < 0) ? widezero : ((~widezero) >> 1)) ^ (ulong)~ieee;
    }

    public static double SortableULongToDobule(ulong lex)
    {
        const ulong widezero = 0;
        long ieee = (long)(((0 <= (long)lex) ? widezero : ((~widezero) >> 1)) ^ ~lex);
        return System.BitConverter.Int64BitsToDouble(ieee);
    }

    public static string EncodeDouble(double d)
    {
        ulong lex = DoubleToSortableULong(d);
        return lex.ToString("X16");
    }

    public static double DecodeDouble(string s)
    {
        ulong lex = ulong.Parse(s, System.Globalization.NumberStyles.AllowHexSpecifier);
        return SortableULongToDobule(lex);
    }

1 个答案:

答案 0 :(得分:3)

为了处理Int64的签名性质,我建议您通过添加-Int64.MinValue将带符号的64位整数转换为无符号的64位值。选择移位保留顺序和移位值以确保结果始终在UInt64变量中表示。

然后使用ToString("D20")格式化为19位十进制数字。或者使用ToString("X16")的16位十六进制数字。

实施这样的转变:

static UInt64 ShiftToUnsigned(Int64 sval)
{
    unchecked
    {
        return (UInt64) (sval - Int64.MinValue);
    }
}

然后转换为字符串可以是:

static string LexicallySortable(Int64 value)
{
    return ShiftToUnsigned(value).ToString("X16");
}

然后这个程序

static void Main(string[] args)
{
    Console.WriteLine(String.Compare(LexicallySortable(Int64.MinValue), LexicallySortable(-2)));
    Console.WriteLine(String.Compare(LexicallySortable(-2), LexicallySortable(-1)));
    Console.WriteLine(String.Compare(LexicallySortable(-1), LexicallySortable(0)));
    Console.WriteLine(String.Compare(LexicallySortable(0), LexicallySortable(1)));
    Console.WriteLine(String.Compare(LexicallySortable(1), LexicallySortable(Int64.MaxValue)));
    Console.ReadLine();
}

根据需要输出一系列-1值。