C# 非指数格式浮点
我有一个UTF-8格式的数据文件,其中包含数千个浮点数。在设计时,开发人员决定省略指数符号中的“e”以节省空间。因此,数据如下所示:C# 非指数格式浮点,c#,string,floating-point,C#,String,Floating Point,我有一个UTF-8格式的数据文件,其中包含数千个浮点数。在设计时,开发人员决定省略指数符号中的“e”以节省空间。因此,数据如下所示: 1.85783+16 0.000000+0 1.900000+6-3.855418-4 1.958263+6 7.836995-4 -2.000000+6 9.903130-4 2.100000+6 1.417469-3 2.159110+6 1.655700-3 2.200000+6 1.813662-3-2.250000+6-1.998687-3 2.30
1.85783+16 0.000000+0 1.900000+6-3.855418-4 1.958263+6 7.836995-4
-2.000000+6 9.903130-4 2.100000+6 1.417469-3 2.159110+6 1.655700-3
2.200000+6 1.813662-3-2.250000+6-1.998687-3 2.300000+6 2.174219-3
2.309746+6 2.207278-3 2.400000+6 2.494469-3 2.400127+6 2.494848-3
-2.500000+6 2.769739-3 2.503362+6 2.778185-3 2.600000+6 3.020353-3
2.700000+6 3.268572-3 2.750000+6 3.391230-3 2.800000+6 3.512625-3
2.900000+6 3.750746-3 2.952457+6 3.872690-3 3.000000+6 3.981166-3
3.202512+6 4.437824-3 3.250000+6 4.542310-3 3.402356+6 4.861319-3
问题是float.Parse()无法使用此格式。我的中间解决方案是
protected static float ParseFloatingPoint(string data)
{
int signPos;
char replaceChar = '+';
// Skip over first character so that a leading + is not caught
signPos = data.IndexOf(replaceChar, 1);
// Didn't find a '+', so lets see if there's a '-'
if (signPos == -1)
{
replaceChar = '-';
signPos = data.IndexOf('-', 1);
}
// Found either a '+' or '-'
if (signPos != -1)
{
// Create a new char array with an extra space to accomodate the 'e'
char[] newData = new char[EntryWidth + 1];
// Copy from string up to the sign
for (int i = 0; i < signPos; i++)
{
newData[i] = data[i];
}
// Replace the sign with an 'e + sign'
newData[signPos] = 'e';
newData[signPos + 1] = replaceChar;
// Copy the rest of the string
for (int i = signPos + 2; i < EntryWidth + 1; i++)
{
newData[i] = data[i - 1];
}
return float.Parse(new string(newData), NumberStyles.Float, CultureInfo.InvariantCulture);
}
else
{
return float.Parse(data, NumberStyles.Float, CultureInfo.InvariantCulture);
}
}
受保护的静态浮点解析浮点(字符串数据)
{
int signPos;
char replaceChar='+';
//跳过第一个字符,以便不捕获前导+字符
signPos=data.IndexOf(replaceChar,1);
//没有找到“+”,所以让我们看看是否有“-”
如果(signPos==-1)
{
replaceChar='-';
signPos=data.IndexOf('-',1);
}
//找到了“+”或“-”
如果(signPos!=-1)
{
//创建一个具有额外空间的新字符数组以容纳“e”
char[]newData=newchar[EntryWidth+1];
//从字符串复制到符号
对于(int i=0;i
我不能调用简单的String.Replace(),因为它将替换任何前导的负号。我可以使用子字符串,但我会制作很多额外的字符串,我很关心性能
有人有更优雅的解决方案吗
string test = "1.85783-16";
char[] signs = { '+', '-' };
int decimalPos = test.IndexOf('.');
int signPos = test.LastIndexOfAny(signs);
string result = (signPos > decimalPos) ?
string.Concat(
test.Substring(0, signPos),
"E",
test.Substring(signPos)) : test;
float.Parse(result).Dump(); //1.85783E-16
我在这里使用的思想确保小数点位于符号之前(这样可以避免指数丢失时出现任何问题),以及使用LastIndexOf()从后面开始工作(确保存在指数时有指数)。如果可能有前缀“+”,则第一个If需要包括| | signPos
其他结果:
"1.85783" => "1.85783"; //Missing exponent is returned clean
"-1.85783" => "-1.85783"; //Sign prefix returned clean
"-1.85783-3" => "-1.85783e-3" //Sign prefix and exponent coexist peacefully.
根据注释,对该方法的测试只显示了5%的性能命中率(在避免使用String.Format()之后,我应该记得这是非常糟糕的)。我认为代码要清楚得多:只需要做一个决定。您是否可以使用正则表达式来识别每个事件 以下是有关合适表达式的一些信息:
就速度而言,您最初的解决方案是我迄今为止尝试过的最快的解决方案(@Godeke's是非常接近的第二个)@Godeke的可读性很强,只是性能下降了一小部分。再加上一些稳健性检查,他的目标可能是长期目标。就稳健性而言,您可以将其添加到您的中,如下所示:
static char[] signChars = new char[] { '+', '-' };
static float ParseFloatingPoint(string data)
{
if (data.Length != EntryWidth)
{
throw new ArgumentException("data is not the correct size", "data");
}
else if (data[0] != ' ' && data[0] != '+' && data[0] != '-')
{
throw new ArgumentException("unexpected leading character", "data");
}
int signPos = data.LastIndexOfAny(signChars);
// Found either a '+' or '-'
if (signPos > 0)
{
// Create a new char array with an extra space to accomodate the 'e'
char[] newData = new char[EntryWidth + 1];
// Copy from string up to the sign
for (int ii = 0; ii < signPos; ++ii)
{
newData[ii] = data[ii];
}
// Replace the sign with an 'e + sign'
newData[signPos] = 'e';
newData[signPos + 1] = data[signPos];
// Copy the rest of the string
for (int ii = signPos + 2; ii < EntryWidth + 1; ++ii)
{
newData[ii] = data[ii - 1];
}
return Single.Parse(
new string(newData),
NumberStyles.Float,
CultureInfo.InvariantCulture);
}
else
{
Debug.Assert(false, "data does not have an exponential? This is odd.");
return Single.Parse(data, NumberStyles.Float, CultureInfo.InvariantCulture);
}
}
为什么不编写一个简单的脚本来重新格式化数据文件一次,然后使用
float.Parse()
您说的是“数千”个浮点数,因此即使是非常简单的方法也会很快完成(如果您说的是“万亿”,我会更犹豫),而只需运行一次的代码(几乎)永远不会对性能造成影响。当然,运行该程序所需的时间比发布问题所需的时间要少,而且出错的机会也要小得多。感谢Godeke不断改进的编辑 最后,我更改了解析函数的参数,使其采用char[]而不是字符串,并使用您的基本前提得出以下结论
protected static float ParseFloatingPoint(char[] data)
{
int decimalPos = Array.IndexOf<char>(data, '.');
int posSignPos = Array.LastIndexOf<char>(data, '+');
int negSignPos = Array.LastIndexOf<char>(data, '-');
int signPos = (posSignPos > negSignPos) ? posSignPos : negSignPos;
string result;
if (signPos > decimalPos)
{
char[] newData = new char[data.Length + 1];
Array.Copy(data, newData, signPos);
newData[signPos] = 'E';
Array.Copy(data, signPos, newData, signPos + 1, data.Length - signPos);
result = new string(newData);
}
else
{
result = new string(data);
}
return float.Parse(result, NumberStyles.Float, CultureInfo.InvariantCulture);
}
受保护的静态浮点解析浮点(char[]数据)
{
int decimalPos=Array.IndexOf(数据“.”);
int posSignPos=Array.LastIndexOf(数据“+”);
int negSignPos=Array.LastIndexOf(数据“-”);
int signPos=(posSignPos>negSignPos)?posSignPos:negSignPos;
字符串结果;
如果(signPos>decimalPos)
{
char[]newData=newchar[data.Length+1];
数组.Copy(数据、新数据、signPos);
newData[signPos]=“E”;
Array.Copy(data,signPos,newData,signPos+1,data.Length-signPos);
结果=新字符串(newData);
}
其他的
{
结果=新字符串(数据);
}
返回float.Parse(result,NumberStyles.float,CultureInfo.InvariantCulture);
}
我将函数的输入从string更改为char[],因为我想离开ReadLine()。我假设这比创建大量字符串性能更好。相反,我从数据文件中获取固定数量的字节(因为它总是11个字符宽度的数据),将字节[]转换为字符[],然后执行上述处理以转换为浮点 对他的语料库的基准测试扩展到25k行,显示大约40%的速度减慢,主要是由于String.Format.Hmmm。与标准连接相比,我看不到任何东西接近于这么大的速度降低,尽管速度较慢。尽管如此,编辑为串联。您是正确的,切换到String.Concat(a,b,c)大大提高了您的性能,仅比Brian的性能低5%。您可以使用
LastIndexOfAny(新[]{'+','-'})
在一次点击中找到signPos
。您真的每次都无条件地将test
分配给result
?如果是这样,请尝试更改内容,以便仅在条件不满足时执行该赋值:字符串结果;如果(signPos>decimalPos)结果=string.Concat(…);其他结果=试验代码>从内部了解一些信息,此输入文件本身无法修改。我非常感谢基准测试。最后,我认为你对Godeke更好的长期解决方案的看法是正确的。我
protected static float ParseFloatingPoint(char[] data)
{
int decimalPos = Array.IndexOf<char>(data, '.');
int posSignPos = Array.LastIndexOf<char>(data, '+');
int negSignPos = Array.LastIndexOf<char>(data, '-');
int signPos = (posSignPos > negSignPos) ? posSignPos : negSignPos;
string result;
if (signPos > decimalPos)
{
char[] newData = new char[data.Length + 1];
Array.Copy(data, newData, signPos);
newData[signPos] = 'E';
Array.Copy(data, signPos, newData, signPos + 1, data.Length - signPos);
result = new string(newData);
}
else
{
result = new string(data);
}
return float.Parse(result, NumberStyles.Float, CultureInfo.InvariantCulture);
}