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

本文关键字:词法 排序 字符串 System Int64 表示 | 更新日期: 2023-09-27 18:25:28

我想将(64位)整数转换为词汇可排序的字符串(即,我可以使用String.Compare对结果字符串进行排序,并获得与直接比较原始值相同的顺序)。Jon Skeet在这里提供了替身的实现。我想对Int64做同样的事情(还有Int32、float、datetime、timespan,但我相信我可以从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);
    }

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

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

然后用ToString("D20")将格式设置为19位十进制数字。或使用ToString("X16")将十六进制数字转换为十六进制数字。

执行这样的转换:

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();
}

根据需要输出一系列CCD_ 6值。