Especificador de ancho de printf para mantener la precisión del valor de punto flotante

103

¿Existe un printfespecificador de ancho que se pueda aplicar a un especificador de punto flotante que formatee automáticamente la salida con el número necesario de dígitos significativos de modo que al escanear la cadena de nuevo, se adquiera el valor de punto flotante original?

Por ejemplo, supongamos que imprimo a floatcon una precisión de 2decimales:

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

Cuando escaneo la salida 0.94, no tengo ninguna garantía de cumplimiento de los estándares de que recuperaré el 0.9375valor de punto flotante original (en este ejemplo, probablemente no lo haré).

Me gustaría una forma de indicar printfque imprima automáticamente el valor de punto flotante con la cantidad necesaria de dígitos significativos para garantizar que se pueda escanear de nuevo al valor original que se le pasó printf.

Podría usar algunas de las macros float.hpara derivar el ancho máximo al que pasar printf, pero ¿ya existe un especificador para imprimir automáticamente con la cantidad necesaria de dígitos significativos , o al menos con el ancho máximo?

Vilhelm Grey
fuente
4
@bobobobo ¿Entonces solo está recomendando que se use una suposición sin aire en lugar de adoptar el enfoque portátil?
1
@ H2CO3 No, no recomendaría usar "una suposición fuera del aire", sugeriría usar la printf( "%f", val );que ya es portátil, eficiente y predeterminada.
bobobobo
2
@bobobobo Para que pueda agregarlo a las respuestas, ¿podría citar la cláusula en el estándar C99 que establece que la declaración printf generará el tipo flotante con la máxima precisión por defecto si no se especifica ninguna precisión?
Vilhelm Gray
1
@VilhelmGray Bueno, a medida que @chux se adentra, hay algunas matemáticas bastante complicadas en cuanto a la precisión real de su particular double. A medida que se doublevuelve extremadamente grande (muy lejos de 1.0), en realidad se vuelve menos preciso en la parte decimal (parte del valor menor que 1.0). Así que realmente no se puede tener una respuesta satisfactoria aquí, porque su pregunta tiene una falsa suposición de que (a saber, que todos los floats / doubles son iguales)
bobobobo
2
@Vilhelm Gray C11dr 5.2.4.2.2 "... número de dígitos decimales, n, de manera que cualquier número de coma flotante con p radix b dígitos pueda redondearse a un número de coma flotante con n dígitos decimales y viceversa sin cambios al valor, p log10 bb es una potencia de 10 ⎡1 + p log10 b⎤ de lo contrario FLT_DECIMAL_DIG 6 DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10 ... "Los 6,10,10 son los valores mínimos .
chux - Reincorporación a Monica

Respuestas:

92

Recomiendo la solución hexadecimal @Jens Gustedt: use% a.

OP quiere "imprimir con la máxima precisión (o al menos hasta el decimal más significativo)".

Un ejemplo simple sería imprimir un séptimo como en:

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

Pero profundicemos ...

Matemáticamente, la respuesta es "0.142857 142857 142857 ...", pero estamos usando números de coma flotante de precisión finita. Supongamos que es binario de doble precisión IEEE 754 . Entonces los OneSeventh = 1.0/7.0resultados en el valor a continuación. También se muestran los doublenúmeros de coma flotante representables anteriores y siguientes .

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

Imprimir la representación decimal exacta de a doubletiene usos limitados.

C tiene 2 familias de macros <float.h>para ayudarnos.
El primer conjunto es el número de dígitos significativos que se imprimirán en una cadena en decimal, por lo que al escanear la cadena, obtenemos el punto flotante original. Se muestran con el valor mínimo de la especificación C y un compilador C11 de muestra .

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)

El segundo conjunto es el número de dígitos significativos que se puede escanear una cadena en un punto flotante y luego se imprime el FP, conservando la misma presentación de cadena. Se muestran con el valor mínimo de la especificación C y un compilador C11 de muestra . Creo que disponible pre-C99.

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

El primer conjunto de macros parece cumplir el objetivo de OP de dígitos significativos . Pero esa macro no siempre está disponible.

#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

El "+ 3" fue el quid de mi respuesta anterior. Se centra en si conociendo la conversión de ida y vuelta string-FP-string (conjunto # 2 macros disponibles C89), ¿cómo se determinarían los dígitos para FP-string-FP (conjunto # 1 macros disponibles después de C89)? En general, agregar 3 fue el resultado.

Ahora, ¿cuántos dígitos significativos imprimir se conocen y se controlan <float.h>?

Para imprimir N dígitos decimales significativos, se pueden utilizar varios formatos.

Con "%e", el campo de precisión es el número de dígitos después del dígito inicial y el punto decimal. Entonces - 1está en orden. Nota: Esto -1no se encuentra en laint Digs = DECIMAL_DIG;

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

Con "%f", el campo de precisión es el número de dígitos después del punto decimal. Para un número como OneSeventh/1000000.0, sería necesario OP_DBL_Digs + 6ver todos los dígitos significativos .

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

Nota: muchos están acostumbrados a "%f". Eso muestra 6 dígitos después del punto decimal; 6 es el valor predeterminado de la pantalla, no la precisión del número.

chux - Restablecer a Monica
fuente
¿Por qué es 1.428571428571428492127e-01 y no 1.428571428571428492127e-0 0 1, el número de dígitos después de 'e' debería ser 3?
user1024
12.12.5 Conversiones de punto flotante dice que la precisión predeterminada para %fes 6.
Jingguo Yao
1
@Jingguo Yao Estoy de acuerdo en que la referencia dice "La precisión especifica cuántos dígitos siguen al carácter del punto decimal para el '% f'". La palabra "precisión" no se no utiliza en un sentido matemático, sino simplemente para definir el número de dígitos después del punto decimal. 1234567890.123, matemáticamente tiene 13 dígitos de precisión o dígitos significativos. 0.000000000123 tiene 3 dígitos de precisión matemática, no 13. Los números de coma flotante están distribuidos logarítmicamente. Esta respuesta usa dígitos significativos y el sentido matemático de precisión .
chux - Reincorporar a Monica el
1
@Slipp D. Thompson "Se muestran con el valor mínimo de la especificación C y un compilador C11 de muestra ".
chux - Restablecer a Monica
1
De hecho, tiene razón: mi truco solo es válido para valores con una magnitud entre 1.0 y 1.0eDBL_DIG, que posiblemente sea el único rango realmente adecuado para imprimir "%f"en primer lugar. Usar "%e"como mostró es, por supuesto, un mejor enfoque en general y efectivamente una respuesta decente (aunque quizás no sea tan bueno como "%a"podría serlo si está disponible, y por supuesto "%a"debería estar disponible si `DBL_DECIMAL_DIG lo está). Siempre he deseado un especificador de formato que siempre redondee exactamente a la precisión máxima (en lugar de los 6 lugares decimales codificados).
Greg A. Woods
66

La respuesta corta para imprimir números de punto flotante sin pérdidas (de modo que puedan leerse exactamente en el mismo número, excepto NaN e Infinity):

  • Si su tipo es flotante: use printf("%.9g", number).
  • Si su tipo es doble: use printf("%.17g", number).

NO lo use %f, ya que eso solo especifica cuántos dígitos significativos después del decimal y truncará los números pequeños. Como referencia, se pueden encontrar los números mágicos 9 y 17 en los float.hque define FLT_DECIMAL_DIGy DBL_DECIMAL_DIG.

ccxvii
fuente
6
¿Podrías explicar el %gespecificador?
Vilhelm Gray
14
% g imprime el número con tantos dígitos como sea necesario para la precisión, prefiriendo la sintaxis exponencial cuando los números son pequeños o grandes (1e-5 en lugar de .00005) y omitiendo los ceros finales (1 en lugar de 1.00000).
ccxvii
4
@truthseeker Para representar un código IEEE 754 binary64 , de hecho es necesario imprimir al menos 15 decimales significativos. Pero la falta de ambigüedad necesita 17 ya que los cambios de precisión en un número binario (en 2, 4, 8, etc.) y un número decimal (en 10, 100, 1000, etc.) nunca están en el mismo número (excepto 1.0). Ejemplo: los 2 doublevalores justo por encima de 0.1: 1.000_0000_0000_0000_2e-01, 1.000_0000_0000_0000_3e-01necesidad 17 dígitos de distinguir.
chux - Reincorporar a Monica
3
@chux: se equivoca acerca del comportamiento de% .16g; es no adecuada para su ejemplo de distinguir 1.000_0000_0000_0000_2e-01 de 1.000_0000_0000_0000_3e-01. Se necesita% .17g.
Don Hatch
1
@Don Hatch estoy de acuerdo "%.16g"es insuficiente y "%.17g"y "%.16e"son suficientes. Los detalles de %g, fueron mal recordados por mí.
chux - Reincorporar a Monica
23

Si solo está interesado en el bit (resp patrón hexadecimal), puede usar el %aformato. Esto te garantiza:

La precisión predeterminada es suficiente para una representación exacta del valor si existe una representación exacta en base 2 y, por lo demás, es lo suficientemente grande para distinguir valores de tipo double.

Debo agregar que esto solo está disponible desde C99.

Jens Gustedt
fuente
16

No, no existe un especificador de ancho de printf para imprimir en punto flotante con la máxima precisión . Déjame explicarte por qué.

La precisión máxima de floaty doublees variable y depende del valor real de floato double.

Se recuerdan floaty doublese almacenan en formato sign.exponent.mantissa . Esto significa que se utilizan muchos más bits para el componente fraccionario para números pequeños que para números grandes.

ingrese la descripción de la imagen aquí

Por ejemplo, floatpuede distinguir fácilmente entre 0.0 y 0.1.

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

Pero floatno tiene idea de la diferencia entre 1e27y 1e27 + 0.1.

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

Esto se debe a que toda la precisión (que está limitada por el número de bits de mantisa) se utiliza para la mayor parte del número, a la izquierda del decimal.

El %.fmodificador solo dice cuántos valores decimales desea imprimir desde el número flotante en lo que respecta al formato . El hecho de que la precisión disponible dependa del tamaño del número depende de usted como programador . printfno puedo / no puede manejar eso por ti.

bobobobo
fuente
2
Esta es una excelente explicación de las limitaciones de imprimir con precisión valores de punto flotante en lugares decimales específicos. Sin embargo, creo que fui demasiado ambiguo con mi elección original de palabras, así que actualicé mi pregunta para evitar el término "máxima precisión" con la esperanza de que aclare la confusión.
Vilhelm Gray
Aún depende del valor del número que está imprimiendo.
bobobobo
3
esto es parcialmente cierto, pero no responde la pregunta y está confundido en cuanto a lo que está preguntando OP. Él pregunta si se puede consultar el número de dígitos [decimales] significativos que floatproporciona, y usted afirma que no existe tal cosa (es decir, que no existe FLT_DIG), lo cual es incorrecto.
@ H2CO3 Quizás deberías editar mi publicación y votar en contra (j / k). Esta respuesta afirma FLT_DIGque no significa nada. Esta respuesta afirma que el número de lugares decimales disponibles depende del valor dentro del flotador .
bobobobo
1
¿Está asumiendo que la letra del formato tiene que ser "f"? No creo que sea necesario. Mi lectura de la pregunta es que el OP está buscando algún especificador de formato printf que produzca un viaje de ida y vuelta sin pérdidas, por lo que la respuesta de @ccxvii ("% .9g" para flotar, "% .17g" para doble) es un buena. Probablemente la pregunta estaría mejor redactada eliminando la palabra "ancho" de ella.
Don Hatch
11

Simplemente use las macros de <float.h>y el especificador de conversión de ancho variable ( ".*"):

float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);
bobobobo
fuente
2
@OliCharlesworth ¿Quieres decir así?printf("%." FLT_DIG "f\n", f);
Vilhelm Gray
3
+1 pero esto funciona mejor para %e, no tan bien para %f: solo si sabe que el valor para imprimir es cercano a 1.0.
Pascal Cuoq
3
%eimprime dígitos significativos para números muy pequeños y %fno lo hace. ej x = 1e-100. %.5fimpresiones 0.00000(una pérdida total de precesión). %.5eimpresiones 1.00000e-100.
chux - Reincorporar a Monica
1
@bobobobo Además, se equivoca porque "da razones más precisas". FLT_DIGse define con el valor al que se define por una razón. Si es 6, es porque floatno puede contener más de 6 dígitos de precisión. Si lo imprime con %.7f, el último dígito no tendrá significado. Piense antes de votar en contra.
5
@bobobobo No, %.6fno es equivalente, porque FLT_DIGno siempre es 6. ¿Y a quién le importa la eficiencia? La E / S ya es cara como el infierno, un dígito con más o menos precisión no creará un cuello de botella.
5

Realizo un pequeño experimento para verificar que la impresión con DBL_DECIMAL_DIGsí conserva exactamente la representación binaria del número. Resultó que para los compiladores y bibliotecas de C que probé, DBL_DECIMAL_DIGes de hecho el número de dígitos requerido, e imprimir incluso con un dígito menos 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;
}

Ejecuto esto con el compilador C de Microsoft 19.00.24215.1 y gcc versión 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1). El uso de un dígito decimal menos reduce a la mitad el número de números que se comparan exactamente iguales. (También verifiqué que rand()tal como se usa produce alrededor de un millón de números diferentes). Aquí están los resultados detallados.

Microsoft C

Se probaron valores de 999507 con 17 dígitos: 999507 se encontraron numéricamente iguales, 999507 se encontraron binarios iguales
Se probaron 999507 valores con 16 dígitos: 545389 se encontraron numéricamente iguales, 545389 se encontraron binarios iguales

GCC

Se probaron 999485 valores con 17 dígitos: 999485 se encontraron numéricamente iguales, 999485 se encontraron binarios iguales
Se probaron 999485 valores con 16 dígitos: 545402 se encontraron numéricamente iguales, 545402 se encontraron binarios iguales
Diomidis Spinellis
fuente
1
"ejecutar esto con el compilador C de Microsoft" -> Ese compilador puede tener RAND_MAX == 32767. Considere u.s[j] = (rand() << 8) ^ rand();o similar para asegurarse de que todos los bits tengan la oportunidad de ser 0 o 1.
chux - Restablezca a Monica el
De hecho, su RAND_MAX es 32767, por lo que su propuesta es correcta.
Diomidis Spinellis
1
Actualicé la publicación para manejar RAND_MAX como lo sugirió @ chux-ReinstateMonica. Los resultados son similares a los obtenidos anteriormente.
Diomidis Spinellis
3

En uno de mis comentarios a una respuesta, lamenté que durante mucho tiempo había querido alguna forma de imprimir todos los dígitos significativos en un valor de punto flotante en forma decimal, de la misma manera que la pregunta. Bueno, finalmente me senté y lo escribí. No es del todo perfecto, y este es un código de demostración que imprime información adicional, pero funciona principalmente para mis pruebas. Por favor, avíseme si usted (es decir, alguien) desea una copia de todo el programa contenedor que lo impulsa para probarlo.

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]);
}
Greg A. Woods
fuente
No me importa si responde a la pregunta o no, es realmente impresionante. Hizo falta pensarlo un poco y debería ser reconocido y elogiado. Tal vez sería bueno que incluyera de alguna manera (ya sea aquí o de otro modo) el código completo para la prueba, pero incluso sin él, este es realmente un buen trabajo. ¡Ten un +1 para eso!
Pryftan
0

Que yo sepa, no existe un algoritmo bien difusa que permite la salida al número necesario de dígitos significativos de tal manera que cuando se escanea la parte posterior de cadena en el valor de punto flotante original se adquirió en dtoa.cescrito por Daniel Gay, que está disponible aquí en Netlib (véase también el papel asociado ). Este código se utiliza, por ejemplo, en Python, MySQL, Scilab y muchos otros.

Stéphane Mottelet
fuente