2015年5月6日水曜日

ftoa関数(ひたすらループする)

浮動小数点数を指定した精度で文字列化する関数です

ftoaが本体です
第1引数:変換する値
第2引数:結果を入れる文字配列
第3引数:指定した長さに足りない場合に0を入れるか(偽で0x20埋め 真で0x30埋め)
第4引数:変換結果の最短長さ
第5引数:小数点以下の長さ

またftoafは第1引数にフォーマットの文字列を取り、第2引数に変換する値を取ります
このフォーマットはprintfの%fに使用するフォーマットに近い書式を設定できます

現在のところNaNやInf等の処理は行っていません
またいくつかのフォーマットを指定してsprintfと同じ結果になるように作っていますが、正しく実装されていない部分や、そもそも実装依存で環境によって違うなどの場合があるかもしれません
ループを多用しているので、コンパイラの最適化に依存しているコードでもあります
また内部で浮動小数点を割ったり掛けたりしているので、その間の演算誤差も無視できない場合があります  とりあえず「sprintfとか使えない環境で臨時に使う」程度で


追記:2015-05-06 15-07
一番最後にp--からp++までの間に入っている四捨五入処理ですが、最後の桁が9で切り上げるときに数字がコロンになってしまう問題があります
一番手っ取り早い対策はこのコードをコメントアウトすることです(もちろん四捨五入はされず、小数点以下切り捨てとされます)
数字が9だった場合は上の桁をインクリメントすればいいのですが、例えば999.96を小数点以下1桁まで表示した場合、繰り上げをすると先頭に1桁増やす必要があります
ということで小さな修正では対応できないので、近いうちにちゃんと作りなおそうと思います
追記ここまで

const char* ftoaf(const char*format, float f) {
    static char buff[30];

    uint8_t Sign = 0;
    uint8_t Zero = 0;
    int Length = 0;
    int PointDecimal = -1;

    if (*format == '%') {
        format++;
    }

    while (*format) {
        if (0) {
        } else if (*format == '+') {
            Sign = 1;
        } else if (*format == '0') {
            Zero = 1;
        } else if (*format == '.') {
            PointDecimal = 0;
            format++;
            while (*format >= '0' && *format <= '9') {
                PointDecimal *= 10;
                PointDecimal += *format - '0';
                format++;
            }
            format--;
        } else if (*format > '0' && *format <= '9') {
            while (*format >= '0' && *format <= '9') {
                Length *= 10;
                Length += *format - '0';
                format++;
            }
            format--;
        } else if (*format == 'f') {
            break;
        }

        format++;
    }

    ftoa(f, buff, Sign, Zero, Length, PointDecimal);

    return(buff);
}

char* ftoa(float f, char *buff, uint8_t Sign, uint8_t Zero, int Length, int PointDecimal) {
    char *p = buff;
    char SignChar = '\0';

    if (f < 0) {
        f = -f;
        if (Zero) { *p++ = '-'; } else { SignChar = '-'; } Length--;
    } else if (Sign) {
        if (Zero) { *p++ = '+'; } else { SignChar = '+'; } Length--;
    } /* else:f‚ª³‚Å‚©‚ÂSign‚ª‹U */

    unsigned int value = (unsigned int)f;
    f -= value;

    {
        if (PointDecimal > 0) {
            Length -= PointDecimal;
            Length--;
        }

        int i;
        for (i = 1; value / i > 0; i *= 10) {
            Length--;
        }

        for (; Length > 0; Length--) {
            *p++ = Zero ? '0' : ' ';
        }

        if (SignChar != '\0') {
            *p++ = SignChar;
        }

        for (i /= 10; i > 0; i /= 10) {
            *p++ = '0' + ((value / i) % 10);
        }
    }

    if (PointDecimal != 0) {
        if (PointDecimal == -1) {
            PointDecimal = 6;
        }

        *p++ = '.';

        int i;
        for (i = 0; i < PointDecimal; i++) {
            f *= 10;
            int j = (int)f;
            f -= j;
            *p++ = '0' + j;
        }

        {
            p--;

            int j = (int)(f * 10);

            if (j == 5) {
                if (((*p - '0') % 2) == 1) {
                    (*p)++;
                }
            } else if (j > 5) {
                (*p)++;
            }

            p++;
        }
    }

    *p = '\0';

    return(buff);
}

0 件のコメント:

コメントを投稿