Pergunta

Eu estou convertendo duplo valores de cadeia de caracteres como esta:

std::string conv(double x) {
    char buf[30];
    sprintf(buf, "%.20g", x);
    return buf;
}

Eu codifiquei o tamanho do buffer de 30, mas não tenho certeza se isso é grande o suficiente para todos os casos.

  • Como posso descobrir o tamanho máximo da memória intermédia que eu preciso?
  • A precisão obter mais alta (e, portanto, buffer de necessidades para aumentar) quando a troca de 32 bits para 64?

PS:Eu não posso usar ostringstream ou boost::lexical_cast para o desempenho razão (ver este)

Foi útil?

Solução

Eu codifiquei o tamanho do buffer de 30, mas não tenho certeza se isso é grande o suficiente para todos os casos.

É.%.20g especifica 20 dígitos na mantissa.adicione 1 para o ponto decimal.1 para o (possível) assinar, 5 para "e+308" ou "e-308", o pior caso de expoente.e 1 para o valor nulo.

20 + 1 + 1 + 5 + 1 = 28.

A precisão obter mais alta (e, portanto, buffer de necessidades para aumentar) quando a troca de 32 bits para 64?

Não.

Um casal é o mesmo tamanho em ambas as arquiteturas.Se você declarar variáveis como long double, então você possivelmente tem 1 dígito a mais no expoente "e+4092", que ainda se encaixa em uma de 30 caracteres do buffer.Mas só em X86, e apenas em processadores mais antigos.

O long double é um obsoletos 80 bits forma de valor de ponto flutuante que foi o formato nativo do 486 FPU.Que FPU arquitetura não escala bem e como já foi descartado em favor do estilo de instruções SSE, onde o maior valor de ponto flutuante de 64 bits duplo.

Que é um longo caminho de dizer um buffer de 30 caracteres sempre vai ser suficiente, enquanto você manter limitar a mantissa em sua impressão a 20 dígitos.

Outras dicas

printf("%.20g", 1.79769e+308); é 1.7976900000000000632e+308, 27 bytes, incluindo o à direita 0. Eu escolheria 64 ou 128 apenas para ter certeza.

(Como está na pilha e lançado logo depois que você também pode usar grandes buffers, até 2048 bytes, sem encontrar problemas para aplicativos não incorporados)

Além disso, tem certeza de que o gargalo do seu programa é lexical_cast..? Fazer o que você está fazendo parece muito bobo para mim

Parece que me lembro que se você ligar sprintf com um NULL destino, não faz nada. No entanto, retorna o número de chars que "escreveu". Se estou certo (e não consigo encontrar a fonte para isso), você pode fazer:

// find the length of the string
int len = sprintf(NULL, fmt, var1, var2,...);
// allocate the necessary memory.
char *output = malloc(sizeof(char) * (len + 1)); // yes I know that sizeof(char) is defined as 1 but this seems nicer.
// now sprintf it after checking for errors
sprintf(output, fmt, var1, var2,...);

Outra opção é usar snprintf que permite limitar o comprimento da saída:

#define MAX 20 /* or whatever length you want */
char output[MAX];
snprintf(output, MAX, fmt, var1, var2,...);

snprintf pega o tamanho do buffer como argumento e não permite que a sequência de saída exceda esse tamanho.

Aqui está um programa para imprimir o número de dígitos necessários para valores máximos e mínimos double pode levar para qualquer sistema:

#include <float.h>
#include <stdio.h>

int main(void)
{
    double m = DBL_MAX;
    double n = DBL_MIN;
    int i;
    i = printf("%.20g\n", m);
    printf("%d\n", i);
    i = printf("%.20g\n", n);
    printf("%d\n", i);
    return 0;
}

Para mim, imprime:

1.7976931348623157081e+308
27
2.2250738585072013831e-308
27

Desde 27, inclui uma nova linha, mas não inclui o término 0 Para as cordas, eu diria que neste sistema, 27 devem ser suficientes. Por long double, a resposta parece ser 27 e 28 para LDBL_MAX e LDBL_MIN respectivamente no meu sistema.

A página do homem (no meu para sprintf diz isso sobre %g:

O argumento duplo é convertido em estilo para e (ou f ou e para conversões G). A precisão especifica o número de dígitos significativos. Se falta a precisão, 6 dígitos serão dados; Se a precisão for zero, ela será tratada como 1. O estilo E é usado se o expoente de sua conversão for menor que -4 ou maior ou igual à precisão. Os zeros à direita são removidos da parte fracionária do resultado; Um ponto decimal aparece apenas se for seguido por pelo menos um dígito.

A redação semelhante está no padrão C.

Então, acho que você estará seguro se usou a saída do programa acima como tamanho da sua matriz.

Se você estiver em uma plataforma que suporta POSIX ou C99, poderá usar snprintf Para calcular o tamanho do buffer de que você precisará. snprintf pega um parâmetro indicando o tamanho do buffer que você está passando; Se o tamanho da string exceder o tamanho desse buffer, ela truncaria a saída para caber no buffer e retornará a quantidade de espaço necessária para ajustar a saída inteira. Você pode usar a saída disso para alocar um buffer do tamanho exato. Se você deseja apenas calcular o tamanho do buffer que precisa, pode passar nulo como o buffer e um tamanho de 0 para calcular quanto espaço você precisa.

int size = snprintf(NULL, 0, "%.20g", x);
char *buf = malloc(size + 1); // Need the + 1 for a terminating null character
snprintf(buf, size + 1, "%.20g", x);

Lembrar de free(buf) Depois de usá -lo para evitar vazamentos de memória.

O problema é que ele não funcionará no Visual Studio, que ainda não suporta o C99. Enquanto eles têm algo como snprintf, se o buffer passado for muito pequeno, não retornará o tamanho necessário, mas retorna -1 Em vez disso, o que é completamente inútil (e não aceita NULL como um buffer, mesmo com um 0 comprimento).

Se você não se importa de truncar, você pode simplesmente usar snprintf com um buffer de tamanho fixo e tenha certeza de que você não vai transbordar:

char buf[30];
snprintf(buf, sizeof(buf), "%.20g", x);

Verifique sua plataforma documentos em snprintf; Em particular, algumas plataformas não podem adicionar um nulo de término no final da string se a string for truncada; portanto, você pode precisar fazer isso você mesmo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top