2013-05-20 6 views
15

私は速度が重要で、メモリが非常に短い組み込みDSPで作業しています。sprintfまたはprintfの最小限の実装

現時点では、sprintfはコード内のどの機能のほとんどのリソースも使用しています。私は単純なテキストをフォーマットするのにそれを使用します:%d, %e, %f, %s、精度やエキゾチックな操作のものは何もありません。

私の使用に適した基本的なsprintfまたはprintf関数を実装するにはどうすればよいですか?

+0

Googleでトピックタイトルを入力すると、ここで多くのヒットが見つかりました。ところで、あなたはいつでも自分のロールを張ることができます。 %eと%fは少し難しいかもしれませんが、%dと%sは簡単です。 – wildplasser

+0

@wildplasserコメントの後に、浮動小数点を使用する必要がありますか? –

+0

はい、私は、DSPは、ADCを介して測定された値を提供するために使用されているので、浮動小数点が必要です。 – Gui13

答えて

8

これは、intを文字表現に変換するためにitoaが存在することを前提としています。fputsは、文字列をどこにでも書き出すことができます。

浮動小数点出力は少なくとも1つの点で不適合です:標準で必要とされるように正しく丸めを行なわないため、内部で1.2399999774として格納されている値が例えば1.234の場合は、 1.2340の代わりに1.2399と表示されます。これによりかなりの労力を節約でき、ほとんどの典型的な目的には十分なままです。

また、これはあなたがについて尋ねた変換に加えて、%c%xサポートしていますが、あなたがそれらを取り除くしたい(と明らかに少しメモリを節約しますそうすること)場合、彼らは削除することは非常に簡単です。

#include <stdarg.h> 
#include <stdio.h> 
#include <string.h> 
#include <windows.h> 

static void ftoa_fixed(char *buffer, double value); 
static void ftoa_sci(char *buffer, double value); 

int my_vfprintf(FILE *file, char const *fmt, va_list arg) { 

    int int_temp; 
    char char_temp; 
    char *string_temp; 
    double double_temp; 

    char ch; 
    int length = 0; 

    char buffer[512]; 

    while (ch = *fmt++) { 
     if ('%' == ch) { 
      switch (ch = *fmt++) { 
       /* %% - print out a single % */ 
       case '%': 
        fputc('%', file); 
        length++; 
        break; 

       /* %c: print out a character */ 
       case 'c': 
        char_temp = va_arg(arg, int); 
        fputc(char_temp, file); 
        length++; 
        break; 

       /* %s: print out a string  */ 
       case 's': 
        string_temp = va_arg(arg, char *); 
        fputs(string_temp, file); 
        length += strlen(string_temp); 
        break; 

       /* %d: print out an int   */ 
       case 'd': 
        int_temp = va_arg(arg, int); 
        itoa(int_temp, buffer, 10); 
        fputs(buffer, file); 
        length += strlen(buffer); 
        break; 

       /* %x: print out an int in hex */ 
       case 'x': 
        int_temp = va_arg(arg, int); 
        itoa(int_temp, buffer, 16); 
        fputs(buffer, file); 
        length += strlen(buffer); 
        break; 

       case 'f': 
        double_temp = va_arg(arg, double); 
        ftoa_fixed(buffer, double_temp); 
        fputs(buffer, file); 
        length += strlen(buffer); 
        break; 

       case 'e': 
        double_temp = va_arg(arg, double); 
        ftoa_sci(buffer, double_temp); 
        fputs(buffer, file); 
        length += strlen(buffer); 
        break; 
      } 
     } 
     else { 
      putc(ch, file); 
      length++; 
     } 
    } 
    return length; 
} 

int normalize(double *val) { 
    int exponent = 0; 
    double value = *val; 

    while (value >= 1.0) { 
     value /= 10.0; 
     ++exponent; 
    } 

    while (value < 0.1) { 
     value *= 10.0; 
     --exponent; 
    } 
    *val = value; 
    return exponent; 
} 

static void ftoa_fixed(char *buffer, double value) { 
    /* carry out a fixed conversion of a double value to a string, with a precision of 5 decimal digits. 
    * Values with absolute values less than 0.000001 are rounded to 0.0 
    * Note: this blindly assumes that the buffer will be large enough to hold the largest possible result. 
    * The largest value we expect is an IEEE 754 double precision real, with maximum magnitude of approximately 
    * e+308. The C standard requires an implementation to allow a single conversion to produce up to 512 
    * characters, so that's what we really expect as the buffer size.  
    */ 

    int exponent = 0; 
    int places = 0; 
    static const int width = 4; 

    if (value == 0.0) { 
     buffer[0] = '0'; 
     buffer[1] = '\0'; 
     return; 
    }   

    if (value < 0.0) { 
     *buffer++ = '-'; 
     value = -value; 
    } 

    exponent = normalize(&value); 

    while (exponent > 0) { 
     int digit = value * 10; 
     *buffer++ = digit + '0'; 
     value = value * 10 - digit; 
     ++places; 
     --exponent; 
    } 

    if (places == 0) 
     *buffer++ = '0'; 

    *buffer++ = '.'; 

    while (exponent < 0 && places < width) { 
     *buffer++ = '0'; 
     --exponent; 
     ++places; 
    } 

    while (places < width) { 
     int digit = value * 10.0; 
     *buffer++ = digit + '0'; 
     value = value * 10.0 - digit; 
     ++places; 
    } 
    *buffer = '\0'; 
} 

void ftoa_sci(char *buffer, double value) { 
    int exponent = 0; 
    int places = 0; 
    static const int width = 4; 

    if (value == 0.0) { 
     buffer[0] = '0'; 
     buffer[1] = '\0'; 
     return; 
    } 

    if (value < 0.0) { 
     *buffer++ = '-'; 
     value = -value; 
    } 

    exponent = normalize(&value); 

    int digit = value * 10.0; 
    *buffer++ = digit + '0'; 
    value = value * 10.0 - digit; 
    --exponent; 

    *buffer++ = '.'; 

    for (int i = 0; i < width; i++) { 
     int digit = value * 10.0; 
     *buffer++ = digit + '0'; 
     value = value * 10.0 - digit; 
    } 

    *buffer++ = 'e'; 
    itoa(exponent, buffer, 10); 
} 

int my_printf(char const *fmt, ...) { 
    va_list arg; 
    int length; 

    va_start(arg, fmt); 
    length = my_vfprintf(stdout, fmt, arg); 
    va_end(arg); 
    return length; 
} 

int my_fprintf(FILE *file, char const *fmt, ...) { 
    va_list arg; 
    int length; 

    va_start(arg, fmt); 
    length = my_vfprintf(file, fmt, arg); 
    va_end(arg); 
    return length; 
} 


#ifdef TEST 

int main() { 

    float floats[] = { 0.0, 1.234e-10, 1.234e+10, -1.234e-10, -1.234e-10 }; 

    my_printf("%s, %d, %x\n", "Some string", 1, 0x1234); 

    for (int i = 0; i < sizeof(floats)/sizeof(floats[0]); i++) 
     my_printf("%f, %e\n", floats[i], floats[i]); 

    return 0; 
} 

#endif 
関連する問題