Eigene printf-Funktion, wie implementier ich Integer und Float Ausgaben?



  • #include <stdarg.h>
    
    int printf(const char*, ...);
    
    int putint(int);
    int putchar(int);
    int putfloat(float);
    int putstring(char*);
    int putpointer(int);
    
    int printf(const char *format, ...)
    {
        va_list list;
        va_start(list, format);
        do
        {
            if(*format != '%')
            {
                putchar(*format);
                ++format;
            }
            else
            {
                ++format;
                switch(*format)
                {
                    case 'd': putint(va_arg(list, int));
                              ++format;
                              break;
                    case 'i': putint(va_arg(list, int));
                              ++format;
                              break;
                    case 'c': putchar(va_arg(list, int));
                              ++format;
                              break;
                    case 's': putstring(va_arg(list, char*));
                              ++format;
                              break;
                    case 'f': putfloat(va_arg(list, double));
                              ++format;
                              break;
                    case 'p': putpointer(va_arg(list, int));
                              ++format;
                              break;
                    case '%': putchar('%');
                              ++format;
                              break;
                    default:  return -1;
                }
            }
        }while(*format);
        va_end(list);
        return 0; /* (Vorläufiger) Ersatz für die Anzahl der Zeichen die ausgegeben wurden */
    }
    
    int putint(int i)
    {
        /* Wie kann ich die Ausgabe von Ganzzahltypen implementieren ? */
    }
    
    int putchar(int c)
    {
        write(1, &c, 1);
        return 1;
    }
    
    int putfloat(float f)
    {
        /* Wie kann ich die Ausgabe von Fließkommazahlen/Fließpunktzahlen implementieren ? */
    }
    
    int putstring(char *s)
    {
        int len = 0;
        do
        {
            putchar(*s);
            ++len;
            ++s;
        }while(*s);
        return len;
    }
    
    int putpointer(int p)
    {
    }
    
    int main()
    {
        printf("123%% Test %c %sEFG",'A',"BCD ");
        return 0;
    }
    

    Das ist erstmal mein Grundgerüst (nur die wesentlichen Dinge sind implementiert), Strings und einzelne Zeichen lassen sich ohne Probleme ausgeben. Meine Fragen stehen im Quellcode. Momentan geht es mir darum wie ich Integer und Float Typen ausgeben kann.

    Weiß Jemand darüber bescheid?





  • net schrieb:

    guckst du: http://www.koders.com/c/fid19A03C6A3C284E32B538A2171C3B6238B688A2F6.aspx?s=__vfprintf

    Danke. So wie ich das sehe läuft die Ausgabe über putc, allerdings hab ich meine Probleme die Funktion zu verstehen, könnte mir die Jemand erklären/auskommentieren?





  • ad net:
    So etwas hat er sich wahrscheinlich nicht erwartet. Es geht ihm, so scheint es mir, um logische Probleme ("Wie gebe ich zeichenweise eine Ganzzahl aus?"), die auf logische Weise gelöst werden müssen. Das Durchackern von POSIX-Implementierungen finde ich in diesem Fall sinnlos.

    Wirzenius wrote this portably, Torvalds fucked it up 🙂

    Ich nehme das (ohne es geprüft zu haben) als Anstoß, zu behaupten, dass die Kodierung eines Double-Wertes für die Ausgabe prinzipell irrelevant ist, weil sämtliche Informationen die auf sprachinterne arithmetische Weise erlangt werden können. Dieses Vorgehen optimiert zwar die Ressourcenausnutzung nicht, sorgt aber für portablen Quelltext.

    ad Gast:
    Die Ausgabe von Ganzzahlen (und in Folge die Ausgabe von Gleitkomma-Werten) wird üblicherweise von einer rekursiven Funktion erledigt. Nennen wird sie printd.
    Aus dem Stahgreif würde ich sie (nur für Ganzzahlen) so formulieren:

    /* n ist in meinem Beispiel 1234 */
    int printd (int n) { 
        /* führende Nullen und Leerzeichen ignorieren */
        /* Vorzeichen schreiben */
        /* mit einem Ausdruck die Einerziffer bestimmen (in diesem Fall "4").
        Der Ausdruck ist zum Beispiel:*/
        ziffer = n % 10;
        /* Wenn n ungleich 0 dann: 
        printd mit einem Zehntel von n aufrufen (also mit 123),
        andernfalls: nichts tun */
        if ((i = n / 10) != 0)
            printd(i);
        /* Wenn alle rekursiven Instanzen soweit durchlaufen sind: die jeweilige Ziffer ausgeben.
        Das erfolgt dann von "innen" nach "aussen", also wird die in letzter
        Intstanz erhaltene Einerziffer (in diesem Fall "1") als erstes ausgegeben.
        Die gewünschte Reihenfolge bleibt also erhalten. */        
        putchar(ziffer + '0');
    }
    

    Das wesentliche Problem an einer nicht rekursiven Funktion ist, dass eine tempräre Zeichenkette erzeugt werden muss, die am Ende von hinten nach vorne gekehrt wird.



  • @cheopz
    Vielen Dank, Du hattest Recht mit deiner Vermutung:
    Es geht ihm, so scheint es mir, um logische Probleme ("Wie gebe ich zeichenweise eine Ganzzahl aus?"), die auf logische Weise gelöst werden müssen.

    Allerdings hab ich gestern Nacht noch eine eigene Lösung gefunden:

    #ifdef __MSDOS__
        #include <io.h>
    #elif __unix__
        #include <unistd.h>
    #endif
    
    #include <stdarg.h>
    
    int printf(const char*, ...);
    
    int putint(int);
    int putchar(int);
    int putfloat(double);
    int putstring(char*);
    int putpointer(void*);
    
    double ceil(double);
    double floor(double);
    
    int printf(const char *format, ...)
    {
        int len = 0;
        va_list list;
        va_start(list, format);
        do
        {
            if(*format != '%')
            {
                putchar(*format);
                ++format;
                ++len;
            }
            else
            {
                ++format;
                switch(*format)
                {
                    case 'd':
                    case 'i': len+=putint(va_arg(list, int));
                              ++format;
                              break;
                    case 'c': len+=putchar(va_arg(list, int));
                              ++format;
                              break;
                    case 's': len+=putstring(va_arg(list, char*));
                              ++format;
                              break;
                    case 'f': len+=putfloat(va_arg(list, double));
                              ++format;
                              break;
                    case 'p': len+=putpointer(va_arg(list, void*));
                              ++format;
                              break;
                    case '%': len+=putchar('%');
                              ++format;
                              break;
                    default:  return -1;
                }
            }
        }while(*format);
        va_end(list);
        return len;
    }
    
    int putint(int i)
    {
        int ilen = 0;
        if(i >= 10)
        {
            putint(i / 10);
            putchar(i % 10 + 48);
            ++ilen;
        }
        else if(i < 0)
        {
            putchar('-');
            putint(i - 2*i);
            ++ilen;
        }
        else
        {
            putchar(i + 48);
            return 1;
        }
        return ilen;
    }
    
    int putchar(int c)
    {
        write(1, &c, 1);
        return 1;
    }
    
    int putfloat(double f)
    {
        int flen = 0,z,v,n;
        v = floor(f);
        n = (f - v) * 1000000;
        flen+=putint(v);
        flen+=putchar('.');
        putchar('[');
        putchar(n);
        putchar(']');
        if(v < 1000 && n == 0)
        {
            for(z = 6;z > 0;--z)
            {
                putchar('0');
                flen+=1;
            }
        }
        else
            flen+=putint(n);
        return flen;
    }
    
    int putstring(char *s)
    {
        int slen = 0;
        do
        {
            putchar(*s);
            ++slen;
            ++s;
        }while(*s);
        return slen;
    }
    
    int putpointer(void *p)
    {
    }
    
    double ceil(double x)
    {
        return (double) (int)x + 1;
    }
    
    double floor(double x)
    {
        return (double) (int)x;
    }
    
    int main()
    {
        int len;
        printf("123%% Test %c %sEFG %d %i %d %i %d %i %d\n",'A',"BCD ",42,122,100000,0,-100000,-122,-42);
        len = printf("ABC 5%d%i %s %f",5,5,"Hallo",65);
        printf("\nlen = %d %d %f %f %f",len,60,ceil(60.55),60.12345,999999999.9);
        return 0;
    }
    

    Momenten gibts es noch ein paar Probleme mit der Genauigkeit von Fließkommazahlen aber da arbeite ich dran.



  • int putfloat(double f)
    {
        int flen = 0,z,v,n;
        v = floor(f);
        n = (f - v) * 1000000;
        flen+=putint(v);
        flen+=putchar('.');
        putchar('[');
        putchar(n);
        putchar(']');
        if(v < 1000 && n == 0)
        {
            for(z = 6;z > 0;--z)
            {
                putchar('0');
                flen+=1;
            }
        }
        else
            flen+=putint(n);
        return flen;
    }
    

    Diese Funktion ist mehr zum Testen. Das Prinzip stimmt aus meiner Sicht, besonders dieser Teil, macht noch Probleme:

    putchar('['); /* Um Nachkomma-Ausgabe vorläufig hervorzuheben */
        putchar(n);
        putchar(']');
        if(v < 1000 && n == 0)
        {
            for(z = 6;z > 0;--z)
            {
                putchar('0');
                flen+=1;
            }
        }
        else
            flen+=putint(n);
    

Anmelden zum Antworten