vacíos - ocultar filas con valor cero en tablas dinámicas excel 2010
Cómo ocultar el cero inicial en printf (7)
El estándar C dice que para los especificadores de formato de punto flotante f
y F
:
Si aparece un carácter de punto decimal, al menos un dígito aparece delante de él.
Creo que si no quieres que aparezca un cero antes del punto decimal, probablemente tendrás que hacer algo como usar snprintf()
para formatear el número en una cadena y eliminar el 0
si la cadena formateada comienza con " 0. " (y de manera similar para "-0."). Luego pase esa cadena formateada a nuestra salida real. O algo así.
Las siguientes salidas 0.23
. ¿Cómo hago para que salga simplemente .23
?
printf( "%8.2f" , .23 );
La biblioteca de Standard C no proporciona esto, por lo que debe escribirlo usted mismo. Esto no es un requisito excepcional, único. Tendrá que escribir funciones similares, tarde o temprano, para recortar ceros y agregar miles de separadores. Por lo tanto, vale la pena no solo obtener los bytes de salida que busca, sino ilustrar de manera más general cómo escribir una biblioteca sólida. Al hacerlo ten en cuenta:
Averigua cómo quieres llamarlo. Algo así escribe una vez pero llama un millón de veces, así que haga la llamada lo más fácil posible.
A continuación, realice el conjunto de pruebas ejercitando todas las alternativas que pueda imaginar.
mientras lo hace, simplemente resuelva el problema para siempre para que nunca tenga que volver a resolverlo (p. ej., no codifique el ancho, la precisión, siga adelante y cree versiones para más-e, formato electrónico, etc.) )
haga que sea seguro para los subprocesos, incluso si no está utilizando subprocesos (un caso específico del punto 3, en realidad)
Así que trabajando hacia atrás: la seguridad de subprocesos requiere la asignación de almacenamiento en la pila, lo que debe hacerse desde la persona que llama. Esto no es bonito o divertido, pero sólo acostúmbrate. Es la manera C Los formatos pueden tener ancho, precisión, algunas banderas y un tipo de conversión (f, e, g). Así que vamos a hacer los parámetros de ancho y precisión. En lugar de parametrizar la API pública por completo, tendré múltiples puntos de entrada que dicen en el nombre de la función qué banderas y tipo de conversión utilizan.
Un motivo favorito es que al pasar en búferes a funciones, la función necesitará saber el tamaño. Pero si lo hace un parámetro separado, es un problema, pero como 1) la persona que llama tiene que escribirlo y 2) la persona que llama puede equivocarse. Así que mi estilo personal es hacer una macro de enmascaramiento que asuma que el búfer 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 forma más simple que se me ocurre para llamarlo, con casos de prueba.
(La nota COUNT () es una macro que he usado semanalmente durante décadas para obtener el número de elementos en una matriz. Estándar C, debería haber tenido algo como esto).
(Nota: aquí uso un dialecto de "Notación Húngara". "D" es un doble. "A" es "array of." "Sz" es un búfer de cadena terminado en NUL, mientras que "psz" es un puntero a uno. La diferencia entre estos dos es que "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 el bucle .
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 ser muy similares, así que hagámoslas llamar 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. (Las funciones paralelas también se escriben para los formatos e y g).
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 ) )
Finalmente la función (interna) que hace el trabajo. Tenga en cuenta que snprintf () necesita un guion bajo en 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
Las pruebas adicionales deben verificar que las funciones funcionan con búferes que son demasiado pequeños.
No es posible hacerlo solo usando printf
. La documentación 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.
Tenga en cuenta que si aparece un punto decimal, al menos un dígito aparece delante de él .
Por lo tanto, parece que tienes que codificar manualmente tu propio formateador.
Parece que no hay una solución fácil. Probablemente usaría algo como el código de abajo. No es el método más rápido, sin embargo, debería funcionar con muchos formatos diferentes. Conserva el número de caracteres y la posición del punto también.
#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);
}
Solo conviértelo 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
#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);
}
double f = 0.23;
assert(f < 0.995 && f >= 0);
printf(".%02u/n" , (unsigned)((f + 0.005) * 100));