在所有基础类型都是字符串的数据格式中,必须将数字类型转换为标准化字符串格式,可以按字母顺序进行比较。例如,如果没有底片,则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
下面接受的答案是正确答案。谢谢你们!
答案 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);
}
这是完整的解决方案,来自字符串:
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);
}
答案 1 :(得分:1)
我认为改进的科学记数法,首先是指数,并使用下划线表示正数,将按词汇顺序排序,与数字顺序相同。
如果需要,您甚至可以附加正常表示,因为后缀不会影响排序。
实施例
E000M3 +3.0
E001M2.7 +27.0
不幸的是,它不适用于负数或负指数。您可以为指数引入偏差,就像IEEE格式在内部使用一样。
答案 2 :(得分:0)
事实证明...... org.apache.solr.util包中包含NumberUtils类。此类具有静态方法,可以执行将双精度(和其他数据值)转换为可排序字符串(以及返回)所需的所有内容。这些方法不容易使用。几点说明:
下面的代码显示了使用此库需要做什么。
String key = NumberUtils.double2sortableStr(35.2);