2

私はchar配列を取得しました。これは、次のようなtxtから読み取られた巨大な配列charp[n]です。

//1.txt
194.919 -241.808 234.896
195.569 -246.179 234.482
194.919 -241.808 234.896
..。

foo(char * p、float x、float y、float z){

}

atof、strtodを使おうとしましたが、配列が大きすぎるとstrlen()を呼び出すため、リアルタイムで時間がかかります。sscanfも非常に遅いです。

コードをデバッグすると、atof()とstrtodの両方がビジュアルスタジオでstrlen()を呼び出していることがわかり、crtコードを確認できます。

strtod() call:
        answer = _fltin2( &answerstruct, ptr, (int)strlen(ptr), 0, 0, _loc_update.GetLocaleT());


atof() call:
        return( *(double *)&(_fltin2( &fltstruct, nptr, (int)strlen(nptr), 0, 0, _loc_update.GetLocaleT())->dval) );

私もstrtokを使おうとしていますが、1.txtのデータを変更しないでください。

したがって、これらすべてをfloat x、y、zに変換する最良の方法は誰にでもあります。

Visual Studio 2008 + WIN7

4

10 に答える 10

1

浮動小数点値の形式について追加の仮定を立てることができる場合は、それらを自分で解析するとパフォーマンスが向上する可能性があります。

指数および入力検証なしで値を解析' 'または分離するためのサンプルコード:'\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++);
于 2010-01-09T16:32:21.483 に答える
1

では、トークン化を自分で行ってから、strtod を呼び出してみてはどうでしょうか。

私が考えているのは、次のようなものです。

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   
}

これに関するいくつかの問題は、トークン化が非常に単純であることです。投稿した形式では機能しますが、形式が異なる場合 (別の行で : を使用して数字を区切る)、機能しません。

もう 1 つの問題は、コードがすべての数字が 64 文字未満であると想定していることです。それよりも長い場合、バッファ オーバーフローが発生します。

また、一時バッファへのコピーはいくらかのオーバーヘッドを追加します (ただし、バッファ全体で常に strlen を実行するオーバーヘッドよりも少ないことを願っています)。元のバッファを変更できないと言っていましたが、一時的な変更を行うことはできますか (つまり、戻る前に元の状態に戻す限り、バッファを変更できます):

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++;
    }
}

この手法は、コピーが不要であり、バッファ オーバーフローの心配がないことを意味します。バッファを一時的に変更する必要があります。うまくいけば、それは

于 2010-01-09T17:16:15.100 に答える
1

このコードをチェックしてください。

科学的表現、「+」記号、または先頭のタブをサポートする必要がない場合は、さらに最適化できます。

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;
}
于 2010-01-09T16:04:58.740 に答える
0

あなたに多くの費用がかかっているかどうかは疑問ですstrlen

比較的制限された範囲に収まる数値を利用できる場合は、次のように、できるだけ少ない計算で自分で解析することをお勧めします。

#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;
}
于 2010-01-10T18:11:39.557 に答える
0

特に悪い標準ライブラリを使用していない限り(最近は不可能ですが、それらはすべて優れています)、より速く実行することはできませんatof.

于 2010-01-09T15:38:34.660 に答える
0

を使用しstrtodます。ほとんどの場合、 は呼び出されません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');
}

これは以下を出力します:

194.919
-241.808
234.896
195.569
-246.179
234.482
194.919
-241.808
234.896

私のマシンで。

于 2010-01-09T15:40:37.477 に答える
0

strod()を呼び出す必要がある理由がわかりませんstrlen()。もちろんそうかもしれませんが、その仕様ではそれを必要とするものは何もありません。そしてstrtod()、FPU プロセッサ固有のものを自分で書くことを除いて、あなたが得るのと同じくらい速いと思います。

于 2010-01-09T15:42:39.643 に答える
0

atof、strtod が strlen を使用するのはなぜだと思いますか? 私はそれらを実装したことはありませんが、なぜ入力文字列の長さを知る必要があるのか​​想像できません。それは彼らにとって何の価値もないでしょう。ジェイソンの答えに従って strtod を使用します。それがそのためです。

はい、非常に大量のテキストがある場合は、変換に時間がかかります。仕方ないよ。

于 2010-01-09T15:45:39.403 に答える
0

他の人が言ったように、標準ライブラリの呼び出しよりもはるかにうまくいくとは思いません。それらは長い間存在しており、非常に高度に最適化されています (まあ、少なくとも適切な実装ではそうあるべきです)。

そうは言っても、私にははっきりしないことがいくつかあります。ファイル全体をメモリに読み込んでから、配列を別の配列に変換していますか? その場合、実行しているシステムにスワッピングでそれを行うのに十分なメモリがあることを確認することをお勧めします。これを行っている場合、それらを保存する代わりにディスクから読み取るときに、一度に 1 行ずつ変換することは可能でしょうか?

プログラムのマルチスレッド化を検討できます。ディスクから行を読み取ってバッファリングする 1 つのスレッドと、行を処理する n スレッド。Dr. Dobb's Journal は、使用できる優れたシングル リーダー/シングル ライター ロックレス キューの実装を公開しました。私はこれを同様のアプリで使用しました。ワーカー スレッドにはそれぞれ入力キューがあり、リーダー スレッドはディスクからデータを読み取り、ラウンド ロビン スタイルでこれらのキューに配置します。

于 2010-01-09T16:00:40.247 に答える
0

次のようなものはどうですか:

#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));
    }
}

有効数字 3 桁までである必要があります。


編集:大きな仮数に注意してください。
再度編集: および負の指数。:-(

于 2010-01-09T16:11:19.333 に答える