任何人都知道如何将一个巨大的字符数组转换为浮点、非常巨大的数组,其性能优于atof/STROD/sscanf
我得到了一个char数组,一个巨大的数组char p[n]从类似txt的文件中读取 //1.txt 194.919 -241.808 234.896 195.569 -246.179 234.482 194.919 -241.808 234.896 ... 我也尝试使用strtok,但是我们不应该更改1.txt中的任何数据 因此,任何一个都有最好的方法将所有这些转换为浮点x,y,z任何人都知道如何将一个巨大的字符数组转换为浮点、非常巨大的数组,其性能优于atof/STROD/sscanf,c,performance,atof,C,Performance,Atof,我得到了一个char数组,一个巨大的数组char p[n]从类似txt的文件中读取 //1.txt 194.919 -241.808 234.896 195.569 -246.179 234.482 194.919 -241.808 234.896 ... 我也尝试使用strtok,但是我们不应该更改1.txt中的任何数据 因此,任何一个都有最好的方法将所有这些转换为浮点x,y,z Visual studio 2008+WIN7只要您没有使用特别糟糕的标准库(这些时候不可能,它们都是好的),就不
Visual studio 2008+WIN7只要您没有使用特别糟糕的标准库(这些时候不可能,它们都是好的),就不可能比使用
atof
更快。它几乎肯定不会调用strlen。为什么它需要知道输入的长度?它只需运行过前导空格,然后使用尽可能多的对浮点文本有意义的字符,然后返回刚好超过该值的指针。你可以看到一个例子,也许你在非最佳地使用它?下面是一个如何使用strtod的示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *p = "1.txt 194.919 -241.808 234.896 195.569 -246.179 234.482 194.919 -241.808 234.896";
char *end = p;
char *q;
double d;
while(*end++ != ' '); // move past "1.txt"
do {
q = end;
d = strtod(q, &end);
printf("%g\n", d);
} while(*end != '\0');
}
在我的机器上。我看不出为什么
strod()
应该调用strlen()
。当然可能,但它的规范中没有要求它,如果它需要的话,我会感到惊讶。我要说的是,除了自己编写一些FPU处理器特定的东西之外,strod()
的速度是最快的。你为什么认为atof,strod使用strlen?我从未实现过它们,但我无法想象为什么它们需要知道输入字符串的长度。这对他们来说毫无价值。我会按照Jason的回答使用strtod。这就是它的目的
是的,如果你有大量的文本,转换需要一些时间。就是这样。正如其他人所说,我认为您不会比标准的库调用做得更好。它们已经存在了很长一段时间,并且是高度优化的(好吧,它们应该是,至少在良好的实现中) 也就是说,有些事情我还不清楚。是否将整个文件读入内存,然后将数组转换为另一个数组?如果是这样,您可能需要检查正在运行的系统是否有足够的内存来进行交换。如果您正在这样做,是否可以在从磁盘读取数据而不是存储数据时一次转换一行数据 <>你可以考虑多线程你的程序。一个线程从磁盘读取和缓冲行,n个线程处理行。Dobb博士的杂志发表了你可以使用的。我在一个类似的应用程序中使用过这个。我的工作线程每个都有一个输入队列,然后读线程从磁盘读取数据,并以循环方式将它们放入这些队列。请查看此代码 如果不需要支持科学表示、“+”符号或前导选项卡,则可以进一步优化它 它不使用strlen或任何其他标准库字符串例程
// convert floating-point value in string represention to it's numerical value
// return false if NaN
// F is float/double
// T is char or wchar_t
// '1234.567' -> 1234.567
template <class F, class T> inline bool StrToDouble(const T* pczSrc, F& f)
{
f= 0;
if (!pczSrc)
return false;
while ((32 == *pczSrc) || (9 == *pczSrc))
pczSrc++;
bool bNegative= (_T('-') == *pczSrc);
if ( (_T('-') == *pczSrc) || (_T('+') == *pczSrc) )
pczSrc++;
if ( (*pczSrc < _T('0')) || (*pczSrc > _T('9')) )
return false;
// todo: return false if number of digits is too large
while ( (*pczSrc >= _T('0')) && (*pczSrc<=_T('9')) )
{
f= f*10. + (*pczSrc-_T('0'));
pczSrc++;
}
if (_T('.') == *pczSrc)
{
pczSrc++;
double e= 0.;
double g= 1.;
while ( (*pczSrc >= _T('0')) && (*pczSrc<=_T('9')) )
{
e= e*10. + (*pczSrc-_T('0'));
g= g*10. ;
pczSrc++;
}
f+= e/g;
}
if ( (_T('e') == *pczSrc) || (_T('E') == *pczSrc) ) // exponent, such in 7.32e-2
{
pczSrc++;
bool bNegativeExp= (_T('-') == *pczSrc);
if ( (_T('-') == *pczSrc) || (_T('+') == *pczSrc) )
pczSrc++;
int nExp= 0;
while ( (*pczSrc >= _T('0')) && (*pczSrc <= _T('9')) )
{
nExp= nExp*10 + (*pczSrc-_T('0'));
pczSrc++;
}
if (bNegativeExp)
nExp= -nExp;
// todo: return false if exponent / number of digits of exponent is too large
f*= pow(10., nExp);
}
if (bNegative)
f= -f;
return true;
}
//将字符串表示中的浮点值转换为其数值
//如果为NaN,则返回false
//F是浮动/双精度
//T是char或wchar\T
// '1234.567' -> 1234.567
模板内联布尔StrToDouble(常数T*pczSrc,F&F)
{
f=0;
if(!pczSrc)
返回false;
而((32==pczSrc)| |(9==pczSrc))
pczSrc++;
bool b负=('u T('-')==*pczSrc);
if(_T('-')=*pczSrc)| |(_T('+')==*pczSrc))
pczSrc++;
如果(*pczSrc<_T('0'))|(*pczSrc>_T('9'))
返回false;
//todo:如果位数太大,则返回false
而((*pczSrc>=_T('0'))和(*pczSrc=_T('0'))和(*pczSrc=_T('0'))和(*pczSrc类似于:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static float frac[] =
{
0.000,
0.001,
0.002,
... // fill in
0.997,
0.998,
0.999,
};
static float exp[] =
{
1e-38,
1e-37,
1e-36,
... // fill in
1e+36,
1e+37,
1e+38,
};
float cvt(char* p)
{
char* d = strchr(p, '.'); // Find the decimal point.
char* e = strchr(p, 'e'); // Find the exponent.
if (e == NULL)
e = strchr(p, 'E');
float num = atoi(p);
if (num > 0) {
num += frac[atoi(d + 1)];
} else {
num -= frac[atoi(d + 1)];
}
if (e)
num *= exp[atoi(e)];
return num;
}
int main()
{
char line[100];
while(gets(line)) {
printf("in %s, out %g\n", line, cvt(line));
}
}
#包括
#包括
#包括
静态浮动压裂[]=
{
0.000,
0.001,
0.002,
…//填写
0.997,
0.998,
0.999,
};
静态浮点exp[]=
{
1e-38,
1e-37,
1e-36,
…//填写
1e+36,
1e+37,
1e+38,
};
浮动cvt(字符*p)
{
char*d=strchr(p,'.');//查找小数点。
char*e=strchr(p,'e');//查找指数。
如果(e==NULL)
e=strchr(p,'e');
float num=atoi(p);
如果(数值>0){
num+=frac[atoi(d+1)];
}否则{
num-=frac[atoi(d+1)];
}
如果(e)
num*=exp[atoi(e)];
返回num;
}
int main()
{
字符行[100];
while(获取(行)){
printf(“输入%s,输出%g\n”,行,cvt(行));
}
}
应为三个有效数字。
编辑:注意大尾数。
再次编辑:和负指数。:-(如果您可以对浮点值的格式进行其他假设,那么自己解析它们可能会提高性能
解析'
或'\n'
的示例代码-不带指数和输入验证的分隔值:
float parsef(const char **str)
{
const char *cc = *str;
_Bool neg = (*cc == '-');
if(neg) ++cc;
float value = 0, e = 1;
for(; *cc != '.'; ++cc)
{
if(*cc == ' ' || *cc == '\n' || !*cc)
{
*str = cc;
return neg ? -value : value;
}
value *= 10;
value += *cc - '0';
}
for(++cc;; ++cc)
{
if(*cc == ' ' || *cc == '\n' || !*cc)
{
*str = cc;
return neg ? -value : value;
}
e /= 10;
value += (*cc - '0') * e;
}
}
示例代码:
const char *str = "42 -15.4\n23.001";
do printf("%f\n", parsef(&str));
while(*str++);
好的,你自己做标记化,然后打电话给strod怎么样
我的想法是这样的:
char *current = ...; // initialited to the head of your character array
while (*current != '\0')
{
char buffer[64];
unsigned int idx = 0;
// copy over current number
while (*current != '\0' && !isspace(*current))
{
buffer[idx++] = *current++;
}
buffer[idx] = '\0';
// move forward to next number
while (*current != '\0' && isspace(*current))
{
current++;
}
// use strtod to convert buffer
}
这其中的一些问题是标记化非常简单。它适用于您发布的格式,但如果格式不同(另一行使用:分隔数字),它将不起作用
另一个问题是,代码假定所有数字都少于64个字符。如果它们更长,则会出现缓冲区溢出
另外,复制到临时缓冲区会增加一些开销(但希望比在整个缓冲区上不断执行strlen的开销要小)。我知道你说过你不能更改原始缓冲区,但你能做一个临时更改吗(即,只要你在返回前将缓冲区恢复到原始状态,缓冲区就可以更改):
这项技术意味着不需要复制,也不需要担心缓冲区溢出。您确实需要临时修改缓冲区;希望这是我怀疑strlen
是否会花费您很多钱
如果你可以利用你的数字在一个相对有限的范围内,那么我建议你自己解析它,尽可能少地进行计算
const char *str = "42 -15.4\n23.001";
do printf("%f\n", parsef(&str));
while(*str++);
char *current = ...; // initialited to the head of your character array
while (*current != '\0')
{
char buffer[64];
unsigned int idx = 0;
// copy over current number
while (*current != '\0' && !isspace(*current))
{
buffer[idx++] = *current++;
}
buffer[idx] = '\0';
// move forward to next number
while (*current != '\0' && isspace(*current))
{
current++;
}
// use strtod to convert buffer
}
char *current = ...; // initialited to the head of your character array
while (*current != '\0')
{
char *next_sep = current;
while (*next_sep != '\0' && !isspace(*next_sep))
{
next_sep++;
}
// save the separator before overwriting it
char tmp = *next_sep;
*next_sep = '\0';
// use strtod on current
// Restore the separator.
*next_sep = tmp;
current = next_sep;
// move forward to next number
while (*current != '\0' && isspace(*current))
{
current++;
}
}
#define DIGIT(c) ((c)>='0' && (c)<='9')
BOOL parseNum(char* *p0, float *f){
char* p = *p0;
int n = 0, frac = 1;
BOOL bNeg = FALSE;
while(*p == ' ') p++;
if (*p == '-'){p++; bNeg = TRUE;}
if (!(DIGIT(*p) || *p=='.')) return FALSE;
while(DIGIT(*p)){
n = n * 10 + (*p++ - '0');
}
if (*p == '.'){
p++;
while(DIGIT(*p)){
n = n * 10 + (*p++ - '0');
frac *= 10;
}
}
*f = (float)n/(float)frac;
if (bNeg) *f = -*f;
*p0 = p;
return TRUE;
}