Indicatore di larghezza Printf per mantenere la precisione del valore a virgola mobile


103

Esiste uno printfspecificatore di larghezza che può essere applicato a uno specificatore in virgola mobile che formatta automaticamente l'output con il numero necessario di cifre significative in modo tale che durante la scansione della stringa venga acquisito il valore in virgola mobile originale?

Ad esempio, supponiamo di stampare a floatcon una precisione di 2cifre decimali:

float foobar = 0.9375;
printf("%.2f", foobar);    // prints out 0.94

Quando eseguo la scansione dell'output 0.94, non ho alcuna garanzia conforme agli standard di recuperare il 0.9375valore in virgola mobile originale (in questo esempio, probabilmente non lo farò).

Vorrei un modo per dire printfdi stampare automaticamente il valore in virgola mobile sul numero necessario di cifre significative per garantire che possa essere scansionato di nuovo al valore originale passato a printf.

Potrei usare alcune delle macro float.hper ricavare la larghezza massima a cui passare printf, ma esiste già uno specificatore per stampare automaticamente il numero necessario di cifre significative o almeno la larghezza massima?


4
@bobobobo Quindi stai solo raccomandando di

1
@ H2CO3 No, non consiglierei di usare "un presupposto fuori aria", suggerirei di usarlo printf( "%f", val );che è già portatile, efficiente e di default.
bobobobo

2
@bobobobo In modo che io possa aggiungerlo alle risposte, potresti citare la clausola nello standard C99 che afferma che l'istruzione printf produrrà il tipo float con la massima precisione per impostazione predefinita se non viene specificata alcuna precisione?
Vilhelm Grey

1
@VilhelmGray Bene, quando @chux entra, c'è un po 'di matematica piuttosto complicata per quanto riguarda la precisione effettiva per il tuo particolare double. Man mano che doublediventi estremamente grande (molto lontano da 1.0), in realtà diventa meno preciso nella parte decimale (porzione di valore inferiore a 1.0). Quindi non puoi davvero avere una risposta soddisfacente qui, perché la tua domanda ha un falso presupposto (vale a dire che tutti i floats / doublei sono creati uguali)
bobobobo

2
@Vilhelm Gray C11dr 5.2.4.2.2 "... numero di cifre decimali, n, in modo tale che qualsiasi numero a virgola mobile con p radix b cifre possa essere arrotondato a un numero a virgola mobile con n cifre decimali e viceversa senza modifiche al valore, p log10 bb è una potenza di 10 ⎡1 + p log10 b⎤ altrimenti FLT_DECIMAL_DIG 6 DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10 ... "I 6,10,10 sono i valori minimi .
chux - Ripristina Monica il

Risposte:


92

Consiglio la soluzione esadecimale @Jens Gustedt: usa% a.

OP vuole “stampare con la massima precisione (o almeno fino al decimale più significativo)”.

Un semplice esempio potrebbe essere quello di stampare un settimo come in:

#include <float.h>
int Digs = DECIMAL_DIG;
double OneSeventh = 1.0/7.0;
printf("%.*e\n", Digs, OneSeventh);
// 1.428571428571428492127e-01

Ma andiamo più a fondo ...

Matematicamente, la risposta è "0,142857 142857 142857 ...", ma stiamo usando numeri in virgola mobile a precisione finita. Supponiamo che il binario a doppia precisione IEEE 754 . Quindi i OneSeventh = 1.0/7.0risultati nel valore di seguito. Sono inoltre mostrati i doublenumeri in virgola mobile rappresentabili precedenti e seguenti .

OneSeventh before = 0.1428571428571428 214571170656199683435261249542236328125
OneSeventh        = 0.1428571428571428 49212692681248881854116916656494140625
OneSeventh after  = 0.1428571428571428 769682682968777953647077083587646484375

La stampa della rappresentazione decimale esatta di a doubleha usi limitati.

C ha 2 famiglie di macro <float.h>per aiutarci.
Il primo set è il numero di cifre significative da stampare in una stringa in decimale, quindi durante la scansione della stringa, otteniamo il punto mobile originale. Sono mostrati con il valore minimo della specifica C e un compilatore C11 di esempio .

FLT_DECIMAL_DIG   6,  9 (float)                           (C11)
DBL_DECIMAL_DIG  10, 17 (double)                          (C11)
LDBL_DECIMAL_DIG 10, 21 (long double)                     (C11)
DECIMAL_DIG      10, 21 (widest supported floating type)  (C99)

Il secondo set è il numero di cifre significative che una stringa può essere scansionata in un punto mobile e quindi il FP stampato, mantenendo la stessa presentazione della stringa. Sono mostrati con il valore minimo della specifica C e un compilatore C11 di esempio . Credo disponibile prima del C99.

FLT_DIG   6, 6 (float)
DBL_DIG  10, 15 (double)
LDBL_DIG 10, 18 (long double)

La prima serie di macro sembra soddisfare l'obiettivo di OP di cifre significative . Ma quella macro non è sempre disponibile.

#ifdef DBL_DECIMAL_DIG
  #define OP_DBL_Digs (DBL_DECIMAL_DIG)
#else  
  #ifdef DECIMAL_DIG
    #define OP_DBL_Digs (DECIMAL_DIG)
  #else  
    #define OP_DBL_Digs (DBL_DIG + 3)
  #endif
#endif

Il "+ 3" era il punto cruciale della mia precedente risposta. È incentrato sulla conoscenza della stringa di conversione di andata e ritorno-FP-stringa (set # 2 macro disponibili C89), come si determinano le cifre per FP-string-FP (set # 1 macro disponibili dopo C89)? In generale, aggiungere 3 è stato il risultato.

Ora quante cifre significative stampare sono note e guidate tramite <float.h>.

Per stampare N cifre decimali significative si possono utilizzare vari formati.

Con "%e", il campo di precisione è il numero di cifre dopo la cifra iniziale e il punto decimale. Quindi - 1è in ordine. Nota: questo -1non è nell'inizialeint Digs = DECIMAL_DIG;

printf("%.*e\n", OP_DBL_Digs - 1, OneSeventh);
// 1.4285714285714285e-01

Con "%f", il campo di precisione è il numero di cifre dopo il punto decimale. Per un numero simile OneSeventh/1000000.0, bisognerebbe OP_DBL_Digs + 6vedere tutte le cifre significative .

printf("%.*f\n", OP_DBL_Digs    , OneSeventh);
// 0.14285714285714285
printf("%.*f\n", OP_DBL_Digs + 6, OneSeventh/1000000.0);
// 0.00000014285714285714285

Nota: molti sono abituati a "%f". Che visualizza 6 cifre dopo il punto decimale; 6 è l'impostazione predefinita di visualizzazione, non la precisione del numero.


perché è 1.428571428571428492127e-01 e non 1.428571428571428492127e-0 0 1, il numero di cifre dopo 'e' dovrebbe essere 3?
user1024

12.12.5 Conversioni in virgola mobile dice che la precisione predefinita per %fè 6.
Jingguo Yao

1
@Jingguo Yao D'accordo che il riferimento dice "La precisione specifica quante cifre seguono il carattere del punto decimale per '% f'". La parola "precisione" viene non usato in senso matematico, ma semplicemente per definire il numero di cifre dopo il punto decimale. 1234567890.123, matematicamente ha 13 cifre di precisione o cifre significative. 0.000000000123 ha 3 cifre di precisione matematica, non 13. I numeri in virgola mobile sono distribuiti logaritmicamente. Questa risposta utilizza cifre significative e il senso matematico della precisione .
chux - Ripristina Monica il

1
@Slipp D. Thompson "Sono mostrati con il valore minimo della specifica C e un compilatore C11 di esempio ."
chux - Ripristina Monica il

1
In effetti hai ragione: il mio trucco è valido solo per valori con una grandezza compresa tra 1.0 e 1.0eDBL_DIG, che è probabilmente l'unico intervallo veramente adatto per la stampa "%f"in primo luogo. Usare "%e"come hai mostrato è ovviamente un approccio migliore a tutto tondo ed effettivamente una risposta decente (anche se forse non è buono come usare "%a"potrebbe essere se è disponibile, e ovviamente "%a"dovrebbe essere disponibile se `DBL_DECIMAL_DIG lo è). Ho sempre desiderato un identificatore di formato che arrotondasse sempre esattamente alla massima precisione (invece delle 6 cifre decimali codificate).
Greg A. Woods

66

La risposta breve per stampare i numeri in virgola mobile senza perdite (in modo che possano essere letti esattamente con lo stesso numero, eccetto NaN e Infinity):

  • Se il tuo tipo è float: usa printf("%.9g", number).
  • Se il tuo tipo è doppio: usa printf("%.17g", number).

NON utilizzare %f, poiché specifica solo quante cifre significative dopo il decimale e troncerà i numeri piccoli. Per riferimento, si possono trovare i numeri magici 9 e 17 in float.hcui definisce FLT_DECIMAL_DIGe DBL_DECIMAL_DIG.


6
Saresti in grado di spiegare lo %gspecificatore?
Vilhelm Grey

14
% g stampa il numero con tutte le cifre necessarie per la precisione, preferendo la sintassi esponenziale quando i numeri sono piccoli o enormi (1e-5 invece di .00005) e saltando gli zeri finali (1 invece di 1.00000).
ccxvii

4
@truthseeker Per rappresentare un codice binario 64 IEEE 754 è effettivamente necessario stampare almeno 15 cifre decimali significative. Ma l'univocità richiede 17 poiché la precisione cambia in un numero binario (a 2,4,8, ecc.) E un numero decimale (a 10,100,1000, ecc.) Non sono mai allo stesso numero (eccetto 1.0). Esempio: i 2 doublevalori appena sopra 0.1: 1.000_0000_0000_0000_2e-01, 1.000_0000_0000_0000_3e-01necessità 17 cifre per distinguere.
chux - Ripristina Monica il

3
@chux - Ti sbagli sul comportamento di% .16g; è non è sufficiente per il tuo esempio di distinguere 1.000_0000_0000_0000_2e-01 da 1.000_0000_0000_0000_3e-01. È necessario% .17g.
Don Hatch

1
@Don Hatch sono d'accordo "%.16g"è insufficiente "%.17g"e "%.16e"sono sufficienti. I dettagli di %g, sono stati ricordati male da me.
chux - Ripristina Monica il

23

Se sei interessato solo al bit (o al modello esadecimale) puoi usare il %aformato. Questo ti garantisce:

La precisione predefinita è sufficiente per una rappresentazione esatta del valore se esiste una rappresentazione esatta in base 2 e altrimenti è sufficientemente grande per distinguere valori di tipo double.

Devo aggiungere che questo è disponibile solo da C99.


16

No, non esiste un tale specificatore di larghezza printf per stampare in virgola mobile con la massima precisione . Lasciami spiegare perché.

La precisione massima di floate doubleè variabile e dipende dal valore effettivo di floato double.

Richiamano floate doublesono memorizzati nel formato sign.exponent.mantissa . Ciò significa che ci sono molti più bit utilizzati per la componente frazionaria per numeri piccoli che per numeri grandi.

inserisci qui la descrizione dell'immagine

Ad esempio, floatpuò facilmente distinguere tra 0,0 e 0,1.

float r = 0;
printf( "%.6f\n", r ) ; // 0.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // 0.100000

Ma floatnon ha idea della differenza tra 1e27e 1e27 + 0.1.

r = 1e27;
printf( "%.6f\n", r ) ; // 999999988484154753734934528.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // still 999999988484154753734934528.000000

Questo perché tutta la precisione (che è limitata dal numero di bit di mantissa) viene utilizzata per la maggior parte del numero, a sinistra del decimale.

Il %.fmodificatore dice solo quanti valori decimali vuoi stampare dal numero float per quanto riguarda la formattazione . Il fatto che l' accuratezza disponibile dipenda dalla dimensione del numero dipende da te come programmatore da gestire. printfnon posso / non gestirlo per te.


2
Questa è un'eccellente spiegazione dei limiti della stampa accurata di valori in virgola mobile in posizioni decimali specifiche. Tuttavia, credo di essere stato troppo ambiguo con la mia scelta di parole originale, quindi ho aggiornato la mia domanda per evitare il termine "massima precisione" nella speranza che possa chiarire la confusione.
Vilhelm Grey

Dipende ancora dal valore del numero che stai stampando.
bobobobo

3
questo è in parte vero, ma non risponde alla domanda e sei confuso su ciò che sta chiedendo OP. Sta chiedendo se si può interrogare il numero di cifre [decimali] significative floatfornite da un , e tu asserisci che non c'è niente del genere (cioè che non c'è FLT_DIG), il che è sbagliato.

@ H2CO3 Forse dovresti modificare il mio post e downvote (j / k). Questa risposta afferma FLT_DIGche non significa nulla. Questa risposta afferma che il numero di cifre decimali disponibili dipende dal valore all'interno del float .
bobobobo

1
Stai assumendo che la lettera del formato debba essere "f"? Non credo sia necessario. La mia lettura della domanda è che l'OP sta cercando qualche specificatore di formato printf che produca un round trip non lossy, quindi la risposta di @ccxvii ("% .9g" per float, "% .17g" per double) è una buona. Probabilmente la domanda sarebbe meglio formulata rimuovendo la parola "larghezza" da essa.
Don Hatch,

11

Usa semplicemente le macro da <float.h>e lo specificatore di conversione a larghezza variabile ( ".*"):

float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);

2
@OliCharlesworth Vuoi dire così:printf("%." FLT_DIG "f\n", f);
Vilhelm Grey

3
+1 ma questo funziona meglio per %e, non così bene per %f: solo se è sa che il valore da stampare è vicino 1.0.
Pascal Cuoq

3
%estampa cifre significative per numeri molto piccoli e %fnon lo fa. ad es x = 1e-100. %.5fstampe 0.00000(una perdita totale di precessione). %.5estampe 1.00000e-100.
chux - Ripristina Monica

1
@bobobobo Inoltre, ti sbagli in quanto "fornisce ragioni più precise". FLT_DIGè definito al valore a cui è definito per un motivo. Se è 6, è perché floatnon è in grado di contenere più di 6 cifre di precisione. Se lo stampi usando %.7f, l'ultima cifra non avrà significato. Pensa prima di votare.

5
@bobobobo No, %.6fnon è equivalente, perché FLT_DIGnon è sempre 6. E a chi importa l'efficienza? L'I / O è già costoso da morire, una cifra in più o in meno di precisione non creerà un collo di bottiglia.

5

Ho eseguito un piccolo esperimento per verificare che la stampa con DBL_DECIMAL_DIGpreservi esattamente la rappresentazione binaria del numero. Si è scoperto che per i compilatori e le librerie C che ho provato, DBL_DECIMAL_DIGè effettivamente il numero di cifre richieste, e stampare anche con una cifra in meno crea un problema significativo.

#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

union {
    short s[4];
    double d;
} u;

void
test(int digits)
{
    int i, j;
    char buff[40];
    double d2;
    int n, num_equal, bin_equal;

    srand(17);
    n = num_equal = bin_equal = 0;
    for (i = 0; i < 1000000; i++) {
        for (j = 0; j < 4; j++)
            u.s[j] = (rand() << 8) ^ rand();
        if (isnan(u.d))
            continue;
        n++;
        sprintf(buff, "%.*g", digits, u.d);
        sscanf(buff, "%lg", &d2);
        if (u.d == d2)
            num_equal++;
        if (memcmp(&u.d, &d2, sizeof(double)) == 0)
            bin_equal++;
    }
    printf("Tested %d values with %d digits: %d found numericaly equal, %d found binary equal\n", n, digits, num_equal, bin_equal);
}

int
main()
{
    test(DBL_DECIMAL_DIG);
    test(DBL_DECIMAL_DIG - 1);
    return 0;
}

Lo eseguo con il compilatore C di Microsoft 19.00.24215.1 e la versione gcc 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1). L'uso di una cifra decimale in meno dimezza il numero di numeri che si confrontano esattamente uguali. (Ho anche verificato che rand()come usato produce effettivamente circa un milione di numeri diversi.) Ecco i risultati dettagliati.

Microsoft C

Valori 999507 testati con 17 cifre: 999507 trovato numericamente uguale, 999507 trovato binario uguale
999507 valori testati con 16 cifre: 545389 trovato numericamente uguale, 545389 trovato binario uguale

GCC

999485 valori testati con 17 cifre: 999485 trovato numericamente uguale, 999485 trovato binario uguale
999485 valori testati con 16 cifre: 545402 trovato numericamente uguale, 545402 trovato binario uguale

1
"eseguilo con il compilatore C di Microsoft" -> Quel compilatore potrebbe avere RAND_MAX == 32767. Considera u.s[j] = (rand() << 8) ^ rand();o
qualcosa di

Infatti, il suo RAND_MAX è 32767, quindi la tua proposta è corretta.
Diomidis Spinellis

1
Ho aggiornato il post per gestire RAND_MAX come suggerito da @ chux-ReinstateMonica. I risultati sono simili a quelli ottenuti prima.
Diomidis Spinellis

3

In uno dei miei commenti a una risposta, mi sono lamentato del fatto che ho desiderato a lungo un modo per stampare tutte le cifre significative in un valore in virgola mobile in forma decimale, più o meno allo stesso modo della domanda. Alla fine mi sono seduto e l'ho scritto. Non è del tutto perfetto, e questo è un codice demo che stampa informazioni aggiuntive, ma funziona principalmente per i miei test. Per favore fatemi sapere se voi (cioè qualcuno) desiderate una copia dell'intero programma wrapper che lo guida per i test.

static unsigned int
ilog10(uintmax_t v);

/*
 * Note:  As presented this demo code prints a whole line including information
 * about how the form was arrived with, as well as in certain cases a couple of
 * interesting details about the number, such as the number of decimal places,
 * and possibley the magnitude of the value and the number of significant
 * digits.
 */
void
print_decimal(double d)
{
        size_t sigdig;
        int dplaces;
        double flintmax;

        /*
         * If we really want to see a plain decimal presentation with all of
         * the possible significant digits of precision for a floating point
         * number, then we must calculate the correct number of decimal places
         * to show with "%.*f" as follows.
         *
         * This is in lieu of always using either full on scientific notation
         * with "%e" (where the presentation is always in decimal format so we
         * can directly print the maximum number of significant digits
         * supported by the representation, taking into acount the one digit
         * represented by by the leading digit)
         *
         *        printf("%1.*e", DBL_DECIMAL_DIG - 1, d)
         *
         * or using the built-in human-friendly formatting with "%g" (where a
         * '*' parameter is used as the number of significant digits to print
         * and so we can just print exactly the maximum number supported by the
         * representation)
         *
         *         printf("%.*g", DBL_DECIMAL_DIG, d)
         *
         *
         * N.B.:  If we want the printed result to again survive a round-trip
         * conversion to binary and back, and to be rounded to a human-friendly
         * number, then we can only print DBL_DIG significant digits (instead
         * of the larger DBL_DECIMAL_DIG digits).
         *
         * Note:  "flintmax" here refers to the largest consecutive integer
         * that can be safely stored in a floating point variable without
         * losing precision.
         */
#ifdef PRINT_ROUND_TRIP_SAFE
# ifdef DBL_DIG
        sigdig = DBL_DIG;
# else
        sigdig = ilog10(uipow(FLT_RADIX, DBL_MANT_DIG - 1));
# endif
#else
# ifdef DBL_DECIMAL_DIG
        sigdig = DBL_DECIMAL_DIG;
# else
        sigdig = (size_t) lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX))) + 1;
# endif
#endif
        flintmax = pow((double) FLT_RADIX, (double) DBL_MANT_DIG); /* xxx use uipow() */
        if (d == 0.0) {
                printf("z = %.*s\n", (int) sigdig + 1, "0.000000000000000000000"); /* 21 */
        } else if (fabs(d) >= 0.1 &&
                   fabs(d) <= flintmax) {
                dplaces = (int) (sigdig - (size_t) lrint(ceil(log10(ceil(fabs(d))))));
                if (dplaces < 0) {
                        /* XXX this is likely never less than -1 */
                        /*
                         * XXX the last digit is not significant!!! XXX
                         *
                         * This should also be printed with sprintf() and edited...
                         */
                        printf("R = %.0f [%d too many significant digits!!!, zero decimal places]\n", d, abs(dplaces));
                } else if (dplaces == 0) {
                        /*
                         * The decimal fraction here is not significant and
                         * should always be zero  (XXX I've never seen this)
                         */
                        printf("R = %.0f [zero decimal places]\n", d);
                } else {
                        if (fabs(d) == 1.0) {
                                /*
                                 * This is a special case where the calculation
                                 * is off by one because log10(1.0) is 0, but
                                 * we still have the leading '1' whole digit to
                                 * count as a significant digit.
                                 */
#if 0
                                printf("ceil(1.0) = %f, log10(ceil(1.0)) = %f, ceil(log10(ceil(1.0))) = %f\n",
                                       ceil(fabs(d)), log10(ceil(fabs(d))), ceil(log10(ceil(fabs(d)))));
#endif
                                dplaces--;
                        }
                        /* this is really the "useful" range of %f */
                        printf("r = %.*f [%d decimal places]\n", dplaces, d, dplaces);
                }
        } else {
                if (fabs(d) < 1.0) {
                        int lz;

                        lz = abs((int) lrint(floor(log10(fabs(d)))));
                        /* i.e. add # of leading zeros to the precision */
                        dplaces = (int) sigdig - 1 + lz;
                        printf("f = %.*f [%d decimal places]\n", dplaces, d, dplaces);
                } else {                /* d > flintmax */
                        size_t n;
                        size_t i;
                        char *df;

                        /*
                         * hmmmm...  the easy way to suppress the "invalid",
                         * i.e. non-significant digits is to do a string
                         * replacement of all dgits after the first
                         * DBL_DECIMAL_DIG to convert them to zeros, and to
                         * round the least significant digit.
                         */
                        df = malloc((size_t) 1);
                        n = (size_t) snprintf(df, (size_t) 1, "%.1f", d);
                        n++;                /* for the NUL */
                        df = realloc(df, n);
                        (void) snprintf(df, n, "%.1f", d);
                        if ((n - 2) > sigdig) {
                                /*
                                 * XXX rounding the integer part here is "hard"
                                 * -- we would have to convert the digits up to
                                 * this point back into a binary format and
                                 * round that value appropriately in order to
                                 * do it correctly.
                                 */
                                if (df[sigdig] >= '5' && df[sigdig] <= '9') {
                                        if (df[sigdig - 1] == '9') {
                                                /*
                                                 * xxx fixing this is left as
                                                 * an exercise to the reader!
                                                 */
                                                printf("F = *** failed to round integer part at the least significant digit!!! ***\n");
                                                free(df);
                                                return;
                                        } else {
                                                df[sigdig - 1]++;
                                        }
                                }
                                for (i = sigdig; df[i] != '.'; i++) {
                                        df[i] = '0';
                                }
                        } else {
                                i = n - 1; /* less the NUL */
                                if (isnan(d) || isinf(d)) {
                                        sigdig = 0; /* "nan" or "inf" */
                                }
                        }
                        printf("F = %.*s. [0 decimal places, %lu digits, %lu digits significant]\n",
                               (int) i, df, (unsigned long int) i, (unsigned long int) sigdig);
                        free(df);
                }
        }

        return;
}


static unsigned int
msb(uintmax_t v)
{
        unsigned int mb = 0;

        while (v >>= 1) { /* unroll for more speed...  (see ilog2()) */
                mb++;
        }

        return mb;
}

static unsigned int
ilog10(uintmax_t v)
{
        unsigned int r;
        static unsigned long long int const PowersOf10[] =
                { 1LLU, 10LLU, 100LLU, 1000LLU, 10000LLU, 100000LLU, 1000000LLU,
                  10000000LLU, 100000000LLU, 1000000000LLU, 10000000000LLU,
                  100000000000LLU, 1000000000000LLU, 10000000000000LLU,
                  100000000000000LLU, 1000000000000000LLU, 10000000000000000LLU,
                  100000000000000000LLU, 1000000000000000000LLU,
                  10000000000000000000LLU };

        if (!v) {
                return ~0U;
        }
        /*
         * By the relationship "log10(v) = log2(v) / log2(10)", we need to
         * multiply "log2(v)" by "1 / log2(10)", which is approximately
         * 1233/4096, or (1233, followed by a right shift of 12).
         *
         * Finally, since the result is only an approximation that may be off
         * by one, the exact value is found by subtracting "v < PowersOf10[r]"
         * from the result.
         */
        r = ((msb(v) * 1233) >> 12) + 1;

        return r - (v < PowersOf10[r]);
}

Non mi interessa se risponde o meno alla domanda: è davvero impressionante da fare. Ci è voluto un po 'di riflessione e dovrebbe essere riconosciuto e lodato. Forse sarebbe bene se includessi in qualche modo (qui o altro) il codice completo per il test, ma anche senza di esso questo è davvero un buon lavoro. Avere un +1 per quello!
Pryftan

0

Per quanto ne so, esiste un algoritmo ben diffuso che consente di restituire il numero necessario di cifre significative in modo tale che durante la scansione della stringa, il valore in virgola mobile originale viene acquisito in dtoa.cscritto da Daniel Gay, che è disponibile qui su Netlib (vedere anche il documento associato ). Questo codice viene utilizzato ad esempio in Python, MySQL, Scilab e molti altri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.