• [^] # Re: Hack de vfscanf(3), quelques réponses

    Posté par . En réponse au message Hack de vfscanf(3), 4 questions. Évalué à 1.

    Merci pour ce commentaire éclairé (dommage qu'on ne puisse pas plusser plusieurs fois), j'ai corrigé tout ça :)

    Par contre le problème avec les floats reste entier... Voilà ce qui se passe quand je fais:


    printf("float, system endianness : %f (0x%x)\n", f, (int) f);
    memcpy(float_buffer, (char *) & f, sizeof(f));
    f = 0;
    r = snscanf(float_buffer, sizeof(long_buffer), "%bf", & f);
    printf("returns %i, i = %f (0x%x)\n", r, f, (int) f);


    Sur ma Slack x86, le programme de test donne ça :
    float, system endianness : 1337.123413 (0x539)
    returns 4, i = 4079200000.000000 (0x80000000)

    Sur Mac OS X, PearPC, ça donne:
    float, system endianness : 1337.123413 (0x539)
    returns 4, i = 1151804416.000000 (0x44a72400)

    Voilà ce qui se passe dans snscanf pour les floats binaires:

    if ((flags & SUPPRESS) == 0) {
    double res; /* d'origine */

    if (flags & BINARY) {
    res = _fetch_float(b, width, flags);
    b += width; bufsize -= width;
    } else {
    *p = '0円';
    res = strtod(buf, (char **) NULL);
    }

    if (flags & LONGDBL)
    *va_arg(ap, long double *) = res;
    else if (flags & LONG)
    *va_arg(ap, double *) = res;
    else
    *va_arg(ap, float *) = res;
    nassigned++;
    }


    Et la fonction de conversion, clone de celle utilisée pour les int:

    static long double _fetch_float(const u_char *b, size_t w, int flags)
    {
    long double ret = 1;
    int sys_endian = 0; /* 0 == big endian, little endian otherwise */

    if (*(char *) & ret == 1) sys_endian = 1;

    if (flags & BIG) sys_endian = 0; else if (flags & LITTLE) sys_endian = 1;

    if (flags & TRUNK) {
    if (
    (sys_endian == 1 && (flags & HIBYTES)) ||
    (sys_endian == 0 && (flags & LOBYTES))
    ) {
    /* should begin from the MSB */
    if (flags & LONGDBL) b += sizeof(long double);
    else if (flags & LONG) b += sizeof(double);
    else b += sizeof(float);
    b -= w;
    }
    }

    ret = 0;

    /* fetch bytes, storing them using the given endianness */
    if (sys_endian) {
    while (w --) ret += ((u_quad_t) b[w]) << (w * CHAR_BIT);
    } else {
    size_t i = 0;
    while (w --) ret += ((u_quad_t) b[w]) << (i ++ * CHAR_BIT);
    }

    return ret;
    }


    Mais le plus étrange, c'est que le code suivant marche:
    r = snscanf(float_buffer, sizeof(long_buffer), "%bi", & f);
    Donc en fait en utilisant le code path des entiers, ça passe même sous Mac (mais bon ça marche que sur les archis où sizeof(int) == sizeof(float)).

    Alors, peut être que c'est les shifts qui passent pas, je vais essayer avec une union, mais bon, ça me trouble que _fetch_int() marche et pas ça :)

    J'ai mis à jour le source sur http://mammouth.tuxfamily.org/vfscanf.c si tu souhaites voir la chose en intégralité.