表示系统的最佳方式是什么?作为可排序字符串的两倍

本文关键字:字符串 排序 两倍 最佳 系统 方式 是什么 表示 | 更新日期: 2023-09-27 18:03:11

在所有底层类型都是字符串的数据格式中,必须将数字类型转换为可以按字母顺序进行比较的标准化字符串格式。例如,值27short可以表示为00027,如果没有负值。

double表示为字符串的最佳方式是什么?在我的例子中,我可以忽略负数,但我很好奇在这两种情况下你是如何表示double的。

根据Jon Skeet的建议,我现在正在使用这个,尽管我不能100%确定它会正确工作:

static readonly string UlongFormatString = new string('0', ulong.MaxValue.ToString().Length);
public static string ToSortableString(this double n)
{
    return BitConverter.ToUInt64(BitConverter.GetBytes(BitConverter.DoubleToInt64Bits(n)), 0).ToString(UlongFormatString);
}
public static double DoubleFromSortableString(this string n)
{
    return BitConverter.Int64BitsToDouble(BitConverter.ToInt64(BitConverter.GetBytes(ulong.Parse(n)), 0));
}

更新2

我已经证实了Jon的怀疑——用这种方法底片不起作用。下面是一些示例代码:

void Main()
{
    var a = double.MaxValue;
    var b = double.MaxValue/2;
    var c = 0d;
    var d = double.MinValue/2;
    var e = double.MinValue;
    Console.WriteLine(a.ToSortableString());
    Console.WriteLine(b.ToSortableString());
    Console.WriteLine(c.ToSortableString());
    Console.WriteLine(d.ToSortableString());
    Console.WriteLine(e.ToSortableString());
}
static class Test
{
    static readonly string UlongFormatString = new string('0', ulong.MaxValue.ToString().Length);
    public static string ToSortableString(this double n)
    {
        return BitConverter.ToUInt64(BitConverter.GetBytes(BitConverter.DoubleToInt64Bits(n)), 0).ToString(UlongFormatString);
    }
}

生成以下输出:

09218868437227405311
09214364837600034815
00000000000000000000
18437736874454810623
18442240474082181119

显然没有按预期排序。

更新3

下面接受的答案是正确的。谢谢你们了!

表示系统的最佳方式是什么?作为可排序字符串的两倍

对于双精度体来说,考虑到巨大的范围(double.MaxValue为1.7976931348623157E+308),填充可能相当尴尬。

字符串表示是否仍然必须是人类可读的,或者只是可逆的?

这提供了一个可逆的转换,导致一个合理的短字符串表示,保留字典顺序-但是从字符串中获取的double值根本不明显。

EDIT: 不要单独使用BitConverter.DoubleToInt64Bits。这将反转负值的顺序。

我相信你可以使用DoubleToInt64Bits执行这个转换,然后一些位旋转,但不幸的是,我不能让它工作现在,我有三个孩子迫切想去公园…


为了使所有内容正确排序,负数需要以一补格式存储,而不是符号大小(否则负数和正数以相反的顺序排序),并且符号位需要翻转(以使负数排序小于正数)。下面的代码应该可以达到这个目的:

static ulong EncodeDouble(double d)
{
    long ieee = System.BitConverter.DoubleToInt64Bits(d);
    ulong widezero = 0;
    return ((ieee < 0)? widezero: ((~widezero) >> 1)) ^ (ulong)~ieee;
}
static double DecodeDouble(ulong lex)
{
    ulong widezero = 0;
    long ieee = (long)(((0 <= (long)lex)? widezero: ((~widezero) >> 1)) ^ ~lex);
    return System.BitConverter.Int64BitsToDouble(ieee);
}

这里的演示:http://ideone.com/JPNPY

完整的解决方案是:

static string EncodeDouble(double d)
{
    long ieee = System.BitConverter.DoubleToInt64Bits(d);
    ulong widezero = 0;
    ulong lex = ((ieee < 0)? widezero: ((~widezero) >> 1)) ^ (ulong)~ieee;
    return lex.ToString("X16");
}
static double DecodeDouble(string s)
{
    ulong lex = ulong.Parse(s, System.Globalization.NumberStyles.AllowHexSpecifier);
    ulong widezero = 0;
    long ieee = (long)(((0 <= (long)lex)? widezero: ((~widezero) >> 1)) ^ ~lex);
    return System.BitConverter.Int64BitsToDouble(ieee);
}

示范:http://ideone.com/pFciY

我相信修改后的科学记数法,将指数放在前面,并使用下划线表示正,将按照与数字相同的顺序对词法进行排序。

如果你愿意,你甚至可以附加正常的表示,因为后缀不会影响排序。

例子
E000M3    +3.0
E001M2.7  +27.0

不幸的是,它对负数或负指数都不起作用。您可以为指数引入一个偏差,就像IEEE格式内部使用的那样。

事实证明…org.apache.solr.util包包含NumberUtils类。该类具有静态方法,可以完成将双精度(和其他数据值)转换为可排序字符串(以及返回)所需的所有操作。这些方法再简单不过了。注意事项:

    当然,NumberUtils是用Java写的(不是c#)。我猜代码可以转换成c#…然而,我不是很精通c#。来源可随时在线获取。
  1. 结果字符串是不可打印的(根本)。
  2. 代码中的注释表明,所有奇异的情况,包括负数和无穷大,应该正确工作。
  3. 我没有做任何基准测试…然而,基于对代码的快速扫描,它应该非常快。

下面的代码显示了使用这个库需要做的事情。

String key = NumberUtils.double2sortableStr(35.2);