Especificador de largura de Printf para manter a precisão do valor de ponto flutuante

103

Existe um printfespecificador de largura que pode ser aplicado a um especificador de ponto flutuante que formata automaticamente a saída para o número necessário de dígitos significativos de forma que, ao digitalizar a string de volta, o valor do ponto flutuante original seja adquirido?

Por exemplo, suponha que eu imprima um float com uma precisão de 2casas decimais:

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

Ao digitalizar a saída 0.94, não tenho garantia de conformidade com os padrões de que receberei o 0.9375valor de ponto flutuante original (neste exemplo, provavelmente não o farei).

Eu gostaria de uma maneira printfde imprimir automaticamente o valor de ponto flutuante para o número necessário de dígitos significativos para garantir que ele possa ser digitalizado de volta ao valor original passado printf.

Eu poderia usar algumas das macros em float.hpara derivar a largura máxima a ser passada printf, mas já existe um especificador para imprimir automaticamente o número necessário de dígitos significativos - ou pelo menos a largura máxima?

Vilhelm Gray
fonte
4
@bobobobo Então você está apenas recomendando que se use uma suposição fora do ar em vez de usar a abordagem portátil?
1
@ H2CO3 Não, eu não recomendaria usar "uma suposição fora do ar", eu sugeriria usar printf( "%f", val );que já é portátil, eficiente e o padrão.
bobobobo
2
@bobobobo Para que eu possa adicioná-lo às respostas, você seria capaz de citar a cláusula no padrão C99 que afirma que a instrução printf produzirá o tipo float com precisão máxima por padrão se nenhuma precisão for especificada?
Vilhelm Gray
1
@VilhelmGray Bem, à medida que @chux entra, há uma matemática bastante complicada quanto à precisão real para o seu particular double. Como o seu doublefica extremamente grande (muito longe de 1,0), ele na verdade se torna menos preciso na parte decimal (parte do valor menor que 1,0). Portanto, você não pode realmente ter uma resposta satisfatória aqui, porque sua pergunta contém uma suposição falsa (ou seja, que todos os floats / doubles são criados iguais)
bobobobo
2
@Vilhelm Gray C11dr 5.2.4.2.2 "... número de dígitos decimais, n, de modo que qualquer número de ponto flutuante com p raiz b dígitos pode ser arredondado para um número de ponto flutuante com n dígitos decimais e vice-versa sem alteração ao valor, p log10 bb é uma potência de 10 ⎡1 + p log10 b⎤ caso contrário FLT_DECIMAL_DIG 6 DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10 ... "Os 6,10,10 são os valores mínimos .
chux - Reintegrar Monica em

Respostas:

92

Eu recomendo a solução hexadecimal @Jens Gustedt: use% a.

OP quer “imprimir com precisão máxima (ou pelo menos até a casa decimal mais significativa)”.

Um exemplo simples seria imprimir um sétimo como em:

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

Mas vamos cavar mais fundo ...

Matematicamente, a resposta é "0,142857 142857 142857 ...", mas estamos usando números de ponto flutuante de precisão finita. Vamos supor que o binário de dupla precisão IEEE 754 . Portanto, os OneSeventh = 1.0/7.0resultados no valor abaixo. Também são mostrados os doublenúmeros de ponto flutuante representáveis ​​anteriores e seguintes .

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

Imprimir a representação decimal exata de umdouble tem usos limitados.

C tem 2 famílias de macros <float.h>para nos ajudar.
O primeiro conjunto é o número de dígitos significativos a serem impressos em uma string em decimal, então, ao digitalizar a string de volta, obtemos o ponto flutuante original. Eles são mostrados com o valor mínimo da especificação C e um compilador C11 de amostra .

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)

O segundo conjunto é o número de dígitos significativos que uma string pode ser digitalizada em um ponto flutuante e então o FP impresso, ainda mantendo a mesma apresentação da string. Eles são mostrados com o valor mínimo da especificação C e um compilador C11 de amostra . Acredito que disponível pré-C99.

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

O primeiro conjunto de macros parece cumprir a meta do OP de dígitos significativos . Mas essa macro nem sempre está disponível.

#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

O "+ 3" foi o ponto crucial da minha resposta anterior. É centrado em saber se a conversão de ida e volta string-FP-string (conjunto # 2 macros disponíveis C89), como alguém determinaria os dígitos para FP-string-FP (conjunto # 1 macros disponíveis após C89)? Em geral, some 3 foi o resultado.

Agora, quantos dígitos significativos imprimir são conhecidos e direcionados <float.h>.

Para imprimir N dígitos decimais significativos, pode-se usar vários formatos.

Com "%e", o campo de precisão é o número de dígitos após o dígito inicial e o ponto decimal. Então - 1está em ordem. Nota: Isso -1não está na inicialint Digs = DECIMAL_DIG;

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

Com "%f", o campo de precisão é o número de dígitos após o ponto decimal. Para um número como OneSeventh/1000000.0, seria necessário OP_DBL_Digs + 6ver todos os 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: muitos são usados ​​para "%f". Isso exibe 6 dígitos após o ponto decimal; 6 é o padrão de exibição, não a precisão do número.

chux - Reintegrar Monica
fonte
por que 1,428571428571428492127e-01 e não 1,428571428571428492127e-0 0 1, o número de dígitos após 'e' deve ser 3?
user1024
12.12.5 Conversões de ponto flutuante diz que a precisão padrão para %fé 6.
Jingguo Yao
1
@Jingguo Yao Concordo que a referência diz "A precisão especifica quantos dígitos seguem o caractere de vírgula decimal para '% f'". A palavra "precisão" não é não usado em um sentido matematicamente, mas simplesmente para definir o número de dígitos depois do ponto decimal. 1234567890.123, matematicamente tem 13 dígitos de precisão ou dígitos significativos. 0,000000000123 tem 3 dígitos de precisão matemática, não 13. Os números de ponto flutuante são distribuídos logaritmicamente. Essa resposta usa dígitos significativos e o senso matemático de precisão .
chux - Reintegrar Monica em
1
@Slipp D. Thompson "São mostrados com o valor mínimo da especificação C e um compilador C11 de amostra ."
chux - Reintegrar Monica em
1
Na verdade, você está correto - meu truque só é válido para valores com magnitude entre 1,0 e 1,0eDBL_DIG, que é indiscutivelmente o único intervalo realmente adequado para impressão "%f"em primeiro lugar. Usar "%e"como você mostrou é, obviamente, uma abordagem melhor em geral e efetivamente uma resposta decente (embora talvez não seja tão bom quanto usar "%a"se estiver disponível, e é claro que "%a"deveria estar disponível se `DBL_DECIMAL_DIG estiver). Sempre desejei um especificador de formato que sempre arredondasse exatamente para a precisão máxima (em vez das 6 casas decimais embutidas em código).
Greg A. Woods
66

A resposta curta para imprimir números de ponto flutuante sem perdas (de modo que eles possam ser lidos de volta exatamente para o mesmo número, exceto NaN e Infinity):

  • Se o seu tipo for flutuante: use printf("%.9g", number) .
  • Se o seu tipo for duplo: use printf("%.17g", number).

NÃO use %f, pois isso especifica apenas quantos dígitos significativos após o decimal e truncará pequenos números. Para referência, os números mágicos 9 e 17 podem ser encontrados nos float.hquais define FLT_DECIMAL_DIGe DBL_DECIMAL_DIG.

ccxvii
fonte
6
Você seria capaz de explicar o %gespecificador?
Vilhelm Gray de
14
% g imprime o número com quantos dígitos forem necessários para a precisão, preferindo a sintaxe exponencial quando os números são pequenos ou grandes (1e-5 em vez de 0,00005) e ignorando quaisquer zeros à direita (1 em vez de 1,00000).
ccxvii
4
@truthseeker Para representar um código IEEE 754 binary64 realmente precisa imprimir pelo menos 15 casas decimais significativas. Mas a inequívoca precisa de 17, pois a precisão muda em um número binário (em 2,4,8, etc.) e um número decimal (em 10,100,1000, etc.) nunca estão no mesmo número (exceto 1,0). Exemplo: os 2 doublevalores imediatamente acima 0.1: 1.000_0000_0000_0000_2e-01, 1.000_0000_0000_0000_3e-01necessidade de distinguir 17 dígitos.
chux - Reintegrar Monica
3
@chux - Você está enganado sobre o comportamento de% .16g; é não adequado para o seu exemplo de distinguir 1.000_0000_0000_0000_2e-01 de 1.000_0000_0000_0000_3e-01. % .17g é necessário.
Don Hatch
1
@Don portal Concordo "%.16g"é insuficiente e "%.17g"e "%.16e"são suficientes. Os detalhes de %g, foram mal lembrados por mim.
chux - Reintegrar Monica em
23

Se você estiver interessado apenas no bit (padrão hexadecimal resp), poderá usar o %aformato. Isso garante a você:

A precisão padrão é suficiente para uma representação exata do valor se uma representação exata na base 2 existir e for suficientemente grande para distinguir valores do tipo double.

Eu teria que acrescentar que isso só está disponível desde C99.

Jens Gustedt
fonte
16

Não, não existe tal especificador de largura de printf para imprimir ponto flutuante com precisão máxima . Deixe-me explicar o porquê.

A precisão máxima de floate doubleé variável e depende do valor real de floatou double.

Lembre-se floate doublesão armazenados em sign.exponent.mantissa formato. Isso significa que há muito mais bits usados ​​para o componente fracionário para números pequenos do que para números grandes.

insira a descrição da imagem aqui

Por exemplo, floatpode distinguir facilmente entre 0,0 e 0,1.

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

Mas floatnão tem ideia da diferença entre 1e27e 1e27 + 0.1.

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

Isso ocorre porque toda a precisão (que é limitada pelo número de bits da mantissa) é usada para a grande parte do número, à esquerda do decimal.

O %.fmodificador apenas diz quantos valores decimais você deseja imprimir a partir do número flutuante no que diz respeito à formatação . O fato de que a precisão disponível depende do tamanho do número depende de você, como programador . printfnão posso / não lida com isso para você.

bobobobo
fonte
2
Esta é uma excelente explicação das limitações da impressão precisa de valores de ponto flutuante em casas decimais específicas. No entanto, acredito que fui muito ambíguo com minha escolha original de palavras, então atualizei minha pergunta para evitar o termo "precisão máxima" na esperança de que possa esclarecer a confusão.
Vilhelm Gray
Ainda depende do valor do número que você está imprimindo.
bobobobo de
3
isso é parcialmente verdadeiro, mas não responde à pergunta e você fica confuso quanto ao que OP está perguntando. Ele está perguntando se alguém pode consultar o número de dígitos [decimais] significativos que um floatfornece, e você afirma que não existe tal coisa (ou seja, que não existe FLT_DIG), o que está errado.
@ H2CO3 Talvez você deva editar minha postagem e votar negativamente (j / k). Esta resposta afirma FLT_DIGnão significa nada. Essa resposta afirma que o número de casas decimais disponíveis depende do valor dentro do float .
bobobobo
1
Você está assumindo que o formato da letra deve ser "f"? Não acho que seja necessário. Minha leitura da pergunta é que o OP está procurando algum especificador de formato printf que produza uma viagem de ida e volta sem perdas, então a resposta de @ccxvii ("% .9g" para float, "% .17g" para double) é um um bom. Provavelmente, a pergunta seria melhor formulada removendo a palavra "largura" dela.
Don Hatch
11

Basta usar as macros de <float.h>e o especificador de conversão de largura variável ( ".*"):

float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);
bobobobo
fonte
2
@OliCharlesworth Você quer dizer assim:printf("%." FLT_DIG "f\n", f);
Vilhelm Gray
3
+1, mas funciona melhor para %e, não tão bem para %f: somente se souber que o valor a ser impresso está próximo de 1.0.
Pascal Cuoq
3
%eimprime dígitos significativos para números muito pequenos e %fnão. por exemplo x = 1e-100. %.5fimpressões 0.00000(uma perda total de precessão). %.5eimpressões 1.00000e-100.
chux - Reintegrar Monica
1
@bobobobo Além disso, você está errado ao dizer que "produz razões mais precisas". FLT_DIGé definido para o valor para o qual é definido por um motivo. Se for 6, é porque floatnão é capaz de conter mais de 6 dígitos de precisão. Se você imprimir usando %.7f, o último dígito não terá significado. Pense antes de votar negativamente.
5
@bobobobo Não, %.6fnão é equivalente, porque FLT_DIGnem sempre é 6. E quem liga pra eficiência? E / S já é caro pra caramba, um dígito a mais ou menos precisão não causará gargalo.
5

Eu executo um pequeno experimento para verificar se a impressão com DBL_DECIMAL_DIGrealmente preserva exatamente a representação binária do número. Descobri que, para os compiladores e bibliotecas C que experimentei, DBL_DECIMAL_DIGé de fato o número de dígitos necessário, e imprimir com um dígito a menos cria um 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;
}

Eu executo isso com o compilador C 19.00.24215.1 da Microsoft e gcc versão 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1). Usar um dígito decimal a menos divide pela metade o número de números comparáveis ​​exatamente iguais. (Eu também verifiquei que, rand()como usado, de fato produz cerca de um milhão de números diferentes.) Aqui estão os resultados detalhados.

Microsoft C

999507 valores testados com 17 dígitos: 999507 encontrados numericamente iguais, 999507 encontrados binários iguais
Testados 999.507 valores com 16 dígitos: 545389 encontrados numericamente iguais, 545389 encontrados binários iguais

GCC

Testado 999485 valores com 17 dígitos: 999485 encontrado numericamente igual, 999485 encontrado binário igual
Testado 999485 valores com 16 dígitos: 545402 encontrados numericamente iguais, 545402 encontrados binários iguais
Diomidis Spinellis
fonte
1
"execute isso com o compilador C da Microsoft" -> Esse compilador pode ter RAND_MAX == 32767. Considere u.s[j] = (rand() << 8) ^ rand();ou algo parecido para garantir que todos os bits tenham uma chance de ser 0 ou 1.
chux - Reintegrar Monica
Na verdade, seu RAND_MAX é 32767, então sua proposta está correta.
Diomidis Spinellis
1
Eu atualizei o post para lidar com RAND_MAX conforme sugerido por @ chux-ReinstateMonica. Os resultados são semelhantes aos obtidos anteriormente.
Diomidis Spinellis
3

Em um de meus comentários a uma resposta, lamentei que há muito desejo alguma maneira de imprimir todos os dígitos significativos em um valor de ponto flutuante na forma decimal, da mesma forma que a pergunta pede. Bem, eu finalmente sentei e escrevi. Não é totalmente perfeito e este é um código de demonstração que imprime informações adicionais, mas funciona principalmente para meus testes. Por favor, deixe-me saber se você (ou seja, alguém) gostaria de uma cópia de todo o programa wrapper que o conduz para teste.

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
fonte
Não me importa se responde à pergunta ou não - é realmente impressionante de se fazer. Demorou um pouco e deve ser reconhecido e elogiado. Talvez fosse bom se você incluísse de alguma forma (aqui ou em outro lugar) o código completo para teste, mas mesmo sem ele, este é realmente um bom trabalho. Tenha um +1 para isso!
Pryftan de
0

Pelo que sei, existe um algoritmo bem difundido que permite a saída para o número necessário de dígitos significativos de modo que, ao digitalizar a string de volta, o valor do ponto flutuante original é adquirido por dtoa.cescrito por Daniel Gay, que está disponível aqui no Netlib (ver também o papel associado ). Este código é usado, por exemplo, em Python, MySQL, Scilab e muitos outros.

Stéphane Mottelet
fonte