2012-01-07 16 views
12

¿Hay alguna forma de imprimir en notación científica con menos de 3 lugares para la parte del número del exponente? El formato 6.1 no afecta exponente pero sólo el número de pieza:C++ cómo obtener "un exponente de un dígito" con printf

var=1.23e-9; 
printf ("%e\n", var); 
printf ("%6.1e\n", var); 

da

1.230000e-009 
1.2e-009 

También he intentado esto en wxWidgets con el formato de la cadena, pero el comportamiento es el mismo.

m_var->SetLabel(wxString::Format(wxT("%6.1e"),var)); 

Lo que me gustaría tener es 1.2e-9.

+7

En realidad, de acuerdo con el estándar y con g ++, debería dar como resultado * dos * dígitos para la parte exponente. –

Respuesta

6

He tenido que hacer esto mucho (escribo analizadores de archivos y algunos formatos de archivos como NITF requieren que almacenes valores numéricos como cadenas).

Lo que haces es un exploit basado en lo que realmente significa matemática base-10 (notación científica): Significa que para todos los números reales y, y = (x) * 10^(N) para un entero N y algunos x en el rango (-1, 1) exclusivo.

Por lo tanto, hace lo siguiente

void PrintScientific(double d) 
{ 
    int exponent = (int)floor(log10(fabs(d))); // This will round down the exponent 
    double base = d * pow(10, -1.0*exponent); 

    printf("%lfE%+01d", base, exponent); 
} 

Se pueden añadir todos los especificadores de formato que necesita para controlar el # de caracteres antes, después de que el "" decimal.

¡NO olvide el paso de redondeo!Así es como funciona, usando las propiedades de base10 y logaritmos (base 10 aquí):

Sea y = x * 10^N =>
log (y) = log (x * 10^N) =>
log (y) = log (x) + log (10^N) => // de Log "producto" regla
log (y) = log (x) + N

Desde x está en el rango (-10, 10) - "()" significa exclusivo (exclusivo), lo que implica que log (x) está en el rango (-1, 1). Entonces, cuando redondeamos para la conversión entera, estamos retirando la contribución "log (x)". Puede obtener la parte "x" del número original, lo que le permite generar el original en cualquier notación científica que desee utilizar.

+0

La matemática tiene resultados inconsistentes en casos de esquina cerca de potencias de 10 y números subnormales y es un problema con 'd' como 0, INF, NAN. También introducirá imprecisiones en 'base'. – chux

12

Según Wikipedia:

El exponente siempre contiene al menos dos dígitos; si el valor es cero, el exponente es 00. En Windows, el exponente contiene tres dígitos de forma predeterminada, p. 1.5e002, pero esto puede ser alterado por Microsoft _ specific _set_output_format function.

_set_output_format

4

con C estándar printf() esto no se puede hacer (y el uso de tres dígitos por defecto parece mal también), por lo menos en C99 (no tengo una versión más reciente en el mano). La cita relevante del estándar C99 está en 7.19.6.1 párrafo 8, formatos e, f:

.... El exponente siempre contiene al menos dos dígitos y solo tantos dígitos como sea necesario para representar el exponente. Si el valor es cero, el exponente es cero. ...

La mejor apuesta para adaptarse a esta [portable] en código utilizando una gran cantidad de estas salidas es el uso de C++ iostreams: aunque el formato por defecto es el mismo que en C, es posible instalar una faceta personalizada en la corriente std::locale que tiene el formato de la manera que necesita. Dicho esto, escribir el código de formato puede no ser del todo trivial. Aunque probablemente solo construí sobre la conversión estándar y luego eliminé el exceso de ceros después del carácter e.

1

Encontré que la respuesta de Zach es el método más rápido y simple y también aplicable a cualquier sistema operativo. Encontré que se necesitaban dos modificaciones en la línea "base =" para que funcione con todos los números. (De lo contrario, nan cuando el exponente es negativo en cygwin). La declaración de impresión extra es solo para la compatibilidad de archivos neutrales patran. Hubiera votado su respuesta, pero acabo de comenzar a usar stackexchange, así que no tengo suficiente "reputación".

void PrintScientific(double d) 
{ 
    int exponent = (int)floor(log10(fabs(d))); // This will round down the exponent 
    double base  = (d * pow(10.0, -1*exponent)); 

if(abs(exponent)<10) 
    printf("%13.9lfE%+01d", base, exponent); 
else 
    printf("%12.9lfE%+01d", base, exponent); 
} 
0

C/C++ especifica dos dígitos como mínimo exponente con printf("%e",...). Para imprimir solo 1, y para tratar con Visual Studio que, de forma predeterminada, imprime al menos 3, se necesita un código adicional.

Considere iostreams @Dietmar Kühl

Si el código C++ todavía quiere utilizar printf() formatos de estilo:

ajustar el valor de un double antes de llamar printf() demasiado a menudo da lugar a problemas de redondeo, cortocircuito gama y fracasos de caso general de esquina como tratar con log10(0.0). También considere la gran double justo cerca de una potencia de 10 donde log10() puede quedar corto, -0.0, INF, NAN.

En este caso, es mejor procesar la cadena.

double var = 1.23e-9; 
    //     - 1 . x e - EEEEE \0 
    #define ExpectedSize (1+1+1+1+1+1+ 5 + 1) 
    char buf[ExpectedSize + 10]; 
    snprintf(buf, sizeof buf, "%.1e", var); 
    char *e = strchr(buf, 'e'); // lucky 'e' not in "Infinity" nor "NaN" 
    if (e) { 
    e++; 
    int expo = atoi(e); 
    snprintf(e, sizeof buf - (e - buf), "%1d", expo); 
    } 
    printf("'%6s'\n", buf); // '1.2e-9' 

Nota: %e es amigable para el procesamiento posterior como su anchura no es tan difícil de manejar como "%f". sprintf(buf, "%f", DBL_MAX) podría ser 1000s de char.

Cuestiones relacionadas