将System.Double表示为可排序字符串的最佳方法是什么?

时间:2011-07-24 13:42:33

标签: c# .net sorting double

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

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

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

3 个答案:

答案 0 :(得分:5)

对于双打,填充可能相当尴尬,因为范围很大(double.MaxValue是1.7976931348623157E + 308)。

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

这给出了一个可逆的转换,导致一个相当短的字符串表示,保留了字典顺序 - 但double值只是来自字符串的情况并不明显。

编辑:不要单独使用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

答案 1 :(得分:1)

我认为改进的科学记数法,首先是指数,并使用下划线表示正数,将按词汇顺序排序,与数字顺序相同。

如果需要,您甚至可以附加正常表示,因为后缀不会影响排序。

实施例

E000M3    +3.0
E001M2.7  +27.0

不幸的是,它不适用于负数或负指数。您可以为指数引入偏差,就像IEEE格式在内部使用一样。

答案 2 :(得分:0)

事实证明...... org.apache.solr.util包中包含NumberUtils类。此类具有静态方法,可以执行将双精度(和其他数据值)转换为可排序字符串(以及返回)所需的所有内容。这些方法不容易使用。几点说明:

  1. 当然,NumberUtils是用Java编写的(不是c#)。我猜这个代码可以转换为c#...但是,我并不精通c#。该来源随时可在线获取。
  2. 结果字符串不可打印(根本)。
  3. 代码中的注释表明所有异国情况,包括负数和无穷大,都应该正常工作。
  4. 我没有做任何基准测试...但是,基于对代码的快速扫描,它应该非常快。
  5. 下面的代码显示了使用此库需要做什么。

    String key = NumberUtils.double2sortableStr(35.2);