2010-04-07 10 views

Respuesta

26

El estándar de C dice que para los f y F formato de punto flotante especificadores:

Si aparece un carácter de punto decimal, al menos un dígito antes de que aparezca.

Creo que si no desea que aparezca un cero antes del punto decimal, es probable que tenga que hacer algo como el uso snprintf() para formatear el número en una cadena, y quita la 0 si el formateado la cadena comienza con "0" (y de manera similar para "-0"). Luego pase esa cadena formateada a nuestra salida real. O algo así.

5

No es posible hacerlo solo usando printf. El documention para printf dice:

f - "double" argument is output in conventional form, i.e. 
    [-]mmmm.nnnnnn 
    The default number of digits after the decimal point is six, 
    but this can be changed with a precision field. If a decimal point 
    appears, at least one digit appears before it. The "double" value is 
    rounded to the correct number of decimal places. 

Nota del Si aparece un punto decimal, al menos un dígito aparece antes de que.

Por lo tanto, parece que tiene que codificar su propio formateador.

4
double f = 0.23; 

assert(f < 0.995 && f >= 0); 
printf(".%02u\n" , (unsigned)((f + 0.005) * 100)); 
+0

Esta es la respuesta. Simple, eficiente. "% 2d" también funciona, si se lanza a un (int). Un buen toque agregar el .005 a la ronda como lo hace% .2f. –

+0

@Brent Foust falla para '0.995 chux

+0

@chux: '.% 02u' debería corregir' ​​0 f> 0.995' no hay ningún cero inicial. Estrictamente hablando hay muchos valores donde el código '+ 0.005' difiere de'% .2f' (además del cero inicial) ej., '0.625' ->' .63' contra '0.62' – jfs

2
#include <stdio.h> 

static void printNoLeadingZeros(double theValue) 
{ 
    char buffer[255] = { '\0' }; 

    sprintf(buffer, "%.2f", theValue); 

    printf("%s\n", buffer + (buffer[0] == '0')); 
} 

int main() 
{ 
    double values[] = { 0.23, .23, 1.23, 01.23, 001.23, 101.23 }; 
    int n   = sizeof(values)/sizeof(values[0]); 
    int i   = 0; 

    while(i < n) 
     printNoLeadingZeros(values[i++]); 

    return(0); 
} 
+0

¿El 'sizeof (values)' no devolverá el tamaño del puntero, no la matriz real? –

+0

@ChristianMann: Depende de si está en el modo C o C++ de su compilador, creo. –

+1

Definitivamente el tamaño de la matriz, independientemente del idioma. Las matrices son algo reacias a decaer a punteros, y no lo harán hasta que las 'fuerce' a hacerlo, por ejemplo pasándolas como un parámetro a una función que acepta un puntero. –

0

Parece que no hay solución fácil. Probablemente usaría algo como el código a continuación. No es el método más rápido, sin embargo, debería funcionar con muchos formatos diferentes. También conserva el número de caracteres y la posición del punto.

#include <stdio.h> 

void fixprint(char *s) 
{ 
     size_t i; 
     i = 1; 
     while (s[i]=='0' || s[i]==' ' || s[i]=='+' || s[i]=='-') { 
       if (s[i]=='0') s[i]=' '; 
       i++; 
     } 
} 

int main() 
{ 
     float x = .23; 
     char s[14]; 
     sprintf(s,"% 8.2f",x); 
     fixprint(s); 
     printf("%s\n",s); 
} 
+0

Esto no funciona con lo que debería ser el caso más fácil 'sprintf (s,"%. 5f ", x)' - parece esperar un espacio en alguna parte? – usr2564301

0

Sólo convertir en un entero con la precisión requerida

double value = .12345678901; // input 
int accuracy = 1000; // 3 digit after dot 
printf(".%d\n", (int)(value * accuracy)); 

Salida:

.123 

example source on pastebin

+1

falla para valores como 0.01, 0.001, números negativos, 'valor> INT_MAX/1000', trunca en lugar de redondos. – chux

+0

No se redondea correctamente. Si 'value = .1239', entonces su aproximación produce' .123', mientras que 'printf ("%. 3f \ n ", value);' produce '0.124'. – stackoverflowuser2010

1

la biblioteca estándar de C no proporciona esta, por lo que tiene que escribirlo usted mismo. Este no es un requisito excepcional y único. Tendrá que escribir funciones similares tarde o temprano para recortar los ceros finales y agregar miles separadores. Por lo tanto, vale la pena no solo obtener los bytes de salida que está buscando, sino también ilustrar de forma más general cómo escribir una biblioteca sólida. Al hacerlo, tenga en cuenta:

  1. descubra cómo quiere llamarlo. Algo como esto escribes una vez, pero llama al un millón de veces, así que haz que las llamadas sean lo más fáciles posible.

  2. luego hacer el conjunto de pruebas ejercicio de todas las alternativas que se pueda imaginar

  3. mientras estás en ello, simplemente resolver el problema para siempre por lo que nunca tendrá que volver una él (por ejemplo, no codificar ancho, precisión, seguir adelante y hacer versiones para leading-plus, formato electrónico, y así sucesivamente)

  4. hacen flujos seguros incluso si usted no está utilizando hilos (un caso concreto de punto 3, en realidad)

Trabajando al revés: la seguridad del subproceso requiere la asignación de almacenamiento en la pila, lo que debe hacerse desde la persona que llama. Esto no es bonito ni divertido, solo acaba de acostumbrarse. Es la forma C. Los formatos pueden tener ancho, precisión, algunos indicadores y un tipo de conversión (f, e, g). Así que vamos a hacer parámetros de ancho y precisión. En lugar de parametrizar completamente la API pública, tendré varios puntos de entrada que dicen en el nombre de la función qué banderas y tipo de conversión utilizan.

Una característica que molesta es que al pasar memorias intermedias a funciones, la función necesitará saber el tamaño. Pero si lo convierte en un parámetro separado, es un dolor en el pero como 1) la persona que llama tiene que escribirlo y 2) la persona que llama puede equivocarse. Así que mi estilo personal es crear una macro de enmascaramiento que asume que el buffer es una matriz de caracteres, no un puntero, y que usa sizeof() para pasar el tamaño a una versión más detallada de la función que toma el tamaño.

Aquí está la maqueta de la manera más simple que puedo pensar para llamarlo, con casos de prueba.

(Nota COUNT() es una macro que he usado por semana durante décadas para obtener el número de elementos de una matriz. Estándar C, debería haber tenido algo como esto.)

(Tenga en cuenta que utilizo un dialecto de "Notación húngara" aquí. "d" es un doble. "a" es "matriz de". "sz" es un búfer de cadena terminado en NUL, mientras que "psz" es un puntero a uno. La diferencia entre estos dos es ese "sz" se puede usar con COUNT() o sizeof() para obtener el tamaño de la matriz, mientras que "psz" no puede. "i" es un número entero y la variable específica "i" se usa para bucles

double ad[] = { 0.0, 1.0, 2.2, 0.3, 0.45, 0.666, 888.99, 
       -1.0, -2.2, -0.3, -0.45, -0.666, -888.99 }; 
char szBuf[20]; 

for (int i = 0; i < COUNT(ad); i++) 
    printf("%s\n", NoLeadingZeroF(4, 2, ad[i], szBuf)); 

for (int i = 0; i < COUNT(ad); i++) 
    printf("%s\n", NoLeadingZeroPlusF(4, 2, ad[i], szBuf)); 

Ahora, las versiones "f" y "+ f" parecen muy similar, así que permite que ambos llamen a una función interna. Aquí están las funciones, que toman el tamaño del búfer, y las macros que lo resuelven por sí mismas. (Funciones paralelas también se escriben para E y G formatos.)

char* NoLeadingZeroFN(int iWidth, int iPrecision, double d, char* szBuf, int iBufLen) { 
    return NoLeadingZeroFmtN("%*.*f", iWidth, iPrecision, d, szBuf, iBufLen); 
} 

char* NoLeadingZeroPlusFN(int iWidth, int iPrecision, double d, char* szBuf, int iBufLen) { 
    return NoLeadingZeroFmtN("%+*.*f", iWidth, iPrecision, d, szBuf, iBufLen); 
} 


#define NoLeadingZeroF(width, precision, number, buf) \ 
     NoLeadingZeroFN((width), (precision), (number), (buf), sizeof(buf)) 

#define NoLeadingZeroPlusF(width, precision, number, buf) \ 
     NoLeadingZeroPlusFN((width), (precision), (number), (buf), sizeof(buf)) 

Por último la función (interna) que hace el trabajo. Tenga en cuenta que snprintf() necesita un subrayado antes de Windows, pero no en Unix.

char* NoLeadingZeroFmtN(char* szFmt, int iWidth, int iPrecision, double d, char* szBuf, int iBufLen) { 

#ifdef WIN32 
    _snprintf(szBuf, iBufLen - 1, szFmt, iWidth, iPrecision, d); 
#else 
    snprintf(szBuf, iBufLen - 1, szFmt, iWidth, iPrecision, d); 
#endif 

    // Some snprintf()'s do not promise to NUL-terminate the string, so do it ourselves. 
    szBuf[ iBufLen - 1 ] = '\0'; 

    // _snprintf() returns the length actually produced, IF the buffer is big enough. 
    // But we don't know it was, so measure what we actually got. 
    char* pcTerminator = strchr(szBuf, '\0'); 

    for (char* pcBuf = szBuf; *pcBuf && *pcBuf != '.'; pcBuf++) 
     if (*pcBuf == '0') { 
      memmove(pcBuf, pcBuf + 1, pcTerminator - pcBuf); 
      break; 
     } 

    return szBuf; 
} 

La salida es:

.00 
1.00 
2.20 
.30 
.45 
.67 
888.99 
-1.00 
-2.20 
-.30 
-.45 
-.67 
-888.99 
+.00 
+1.00 
+2.20 
+.30 
+.45 
+.67 
+888.99 
-1.00 
-2.20 
-.30 
-.45 
-.67 
-888.99 

pruebas adicionales debería verificar que las funciones trabajan con tampones que son demasiado pequeñas.

Cuestiones relacionadas