2012-06-05 24 views
19

Estoy escribiendo un programa que imprime literales de punto flotante para ser utilizado dentro de otro programa.flotante de impresión, preservando la precisión

¿Cuántos dígitos necesito imprimir para preservar la precisión del flotador original?

Dado que un flotador tiene 24 * (log(2)/log(10)) = 7.2247199 dígitos decimales de precisión, mi idea inicial fue que la impresión de 8 dígitos debería ser suficiente. Pero si no tengo suerte, esos 0.2247199 se distribuyen a la izquierda y a la derecha de los 7 dígitos significativos, por lo que probablemente debería imprimir 9 dígitos decimales.

¿Mi análisis es correcto? ¿Son suficientes 9 decimales para todos los casos? Me gusta printf("%.9g", x);?

¿Hay una función estándar que convierta un flotador en una cadena con el número mínimo de dígitos decimales requerido para ese valor, en el caso de que 7 u 8 sean suficientes, así que no imprimo dígitos innecesarios?

Nota: No puedo usar literales de coma flotante hexadecimales, porque C++ estándar no los admite.

+1

Uso 1000 dígitos y el clip de los ceros! ;) –

+4

ya que no se puede convertir un flotante basado en binarios a una fracción decimal sin un error, yo propondría simplemente eliminar la representación binaria (o un exponente mantissa + por separado). – Vlad

+5

@Vlad no puedes? ¿No son todas las fracciones binarias representables como un decimal finito? –

Respuesta

16

Con el fin de garantizar que un binary-> decimal-> ida y vuelta binaria recupera el valor binario original, IEEE 754 requiere


The original binary value will be preserved by converting to decimal and back again using:[10] 

    5 decimal digits for binary16 
    9 decimal digits for binary32 
    17 decimal digits for binary64 
    36 decimal digits for binary128 

For other binary formats the required number of decimal digits is 

    1 + ceiling(p*log10(2)) 

where p is the number of significant bits in the binary format, e.g. 24 bits for binary32. 

En C, las funciones que pueden utilizarse para estas conversiones son snprintf() y strtof/strtod/strtold().

Por supuesto, en algunos casos, incluso más dígitos pueden ser útiles (no, no siempre son "ruidosos", dependiendo de la implementación de las rutinas de conversión decimal como snprintf()). Considere, por ejemplo, printing dyadic fractions.

+0

+1 Para la respuesta de prueba estándar (aunque suponiendo una implementación compatible con IEEE, pero que no usa flotadores IEEE de todos modos). –

+0

@ChristianRau: todo el mundo usa más o menos flotadores IEEE estos días sí. Sin embargo, la advertencia es si sus funciones de conversión decimal se redondean correctamente para todas las entradas, lo que es quizás menos cierto.Pero si necesita una representación decimal, no hay mucho que pueda hacer al respecto (excepto implementar sus propias conversiones decimales sin errores, ¡buena suerte!). – janneb

+0

@ChristianRau, janneb: No es universal. Todavía de vez en cuando tengo que lidiar con datos sísmicos que se han generado en formato de coma flotante de IBM. –

1

Se garantiza que la conversión de coma flotante a decimal utilizada en Java produce el menor número de dígitos decimales más allá del punto decimal necesario para distinguir el número de sus vecinos (más o menos).

Puede copiar el algoritmo de aquí: http://www.docjar.com/html/api/sun/misc/FloatingDecimal.java.html atención atención a la FloatingDecimal(float) constructor y el método toJavaFormatString().

+7

¿En qué parte de esas más de 2800 líneas de código comienza el algoritmo? – Gabe

+0

Técnicamente GPL gravados – sehe

+0

@Gabe Se inicia en el constructor y finaliza en el método que indico. La cadena completa es devuelta por la expresión 'new FloatingDecimal (number) .toJavaFormatString()'. – Joni

0

Puede usar sprintf. No estoy seguro de si esto responde a su pregunta exactamente embargo, pero de todos modos, aquí está el código de ejemplo

#include <stdio.h> 
int main(void) 
{ 
float d_n = 123.45; 
char s_cp[13] = { '\0' }; 
char s_cnp[4] = { '\0' }; 
/* 
* with sprintf you need to make sure there's enough space 
* declared in the array 
*/ 
sprintf(s_cp, "%.2f", d_n); 
printf("%s\n", s_cp); 
/* 
* snprinft allows to control how much is read into array. 
* it might have portable issues if you are not using C99 
*/ 
snprintf(s_cnp, sizeof s_cnp - 1 , "%f", d_n); 
printf("%s\n", s_cnp); 
getchar(); 
return 0; 
} 
/* output : 
* 123.45 
* 123 
*/ 
+3

Este es un enfoque sensato, pero una cosa que hemos encontrado con el uso de sprintf es que el redondeo puede ser diferente para diferentes plataformas. –

+0

@Richard Sí Derecho sobre esto ... – kapilddit

3

24 * (log (2)/log (10)) = 7,2247199

Eso es bastante representativo para el problema. No tiene sentido expresar la cantidad de dígitos significativos con una precisión de 0.0000001 dígitos. Está convirtiendo números en texto para el beneficio de humano, no en una máquina. Un ser humano no le importa, y preferiría mucho más, si usted escribió

24 * (log (2)/log (10)) = 7

Tratando de mostrar 8 dígitos significativos sólo genera dígitos de ruido aleatorio. Con probabilidades distintas de cero, 7 ya es demasiado porque el error de coma flotante se acumula en los cálculos. Sobre todo, imprima números usando una unidad de medida razonable. La gente está interesada en milímetros, gramos, libras, pulgadas, etcétera. A ningún arquitecto le importará el tamaño de una ventana expresada con más precisión que 1 mm. Ninguna planta de fabricación de ventanas hará promesa una ventana del tamaño tan preciso como eso.

Por último, no puede ignorar la precisión de los números que introduce en su programa. No es posible medir la velocidad de un trago europeo sin carga de hasta 7 dígitos. Es aproximadamente 11 metros por segundo, 2 dígitos en el mejor de los casos. Así que realizar cálculos a esa velocidad e imprimir un resultado que tenga más dígitos significativos produce resultados sin sentido que prometen precisión que no están ahí.

+10

* para ser utilizado dentro de otro programa * => parece que su suposición de que un humano lee la salida es erróneo. –

+0

Hmm, eso de hecho no se registró. Algo extraño de hacer Bueno, solución fácil, mientras un humano nunca lo vea, imprima dígitos en abundancia. –

+1

Odio cuando parece que mi cerebro editó una palabra importante en el texto: x También parece que Fred estaba preocupado por el consumo de espacio (y supongo que por el rendimiento). –

-1

Con algo así como

def f(a): 
    b=0 
    while a != int(a): a*=2; b+=1 
    return a, b 

(que es Python) que debe ser capaz de obtener mantisa y exponente de una manera libre de pérdidas.

En C, esto probablemente sería

struct float_decomp { 
    float mantissa; 
    int exponent; 
} 

struct float_decomp decomp(float x) 
{ 
    struct float_decomp ret = { .mantissa = x, .exponent = 0}; 
    while x != floor(x) { 
     ret.mantissa *= 2; 
     ret.exponent += 1; 
    } 
    return ret; 
} 

Pero tenga en cuenta que todavía no todos los valores pueden ser representados de esta manera, es sólo un tiro rápido que debe dar la idea, pero probablemente necesita una mejora.

+0

Es muy útil para -1 sin ningún comentario. – glglgl

2

Si el programa está destinado a ser leído por una computadora, haría el truco simple de usar char* aliasing.

  • alias float*-char*
  • copia en un unsigned (o cualquier tipo sin signo es suficientemente grande) a través de char* aliasing
  • imprimir el valor unsigned

La descodificación se acaba de invertir el proceso (y en la mayoría de las plataformas se puede usar un reinterpret_cast directo).

+0

¿Seguramente aliasing directamente a 'unsigned *' también funcionaría? – huon

+2

¿Y de qué manera da esto un literal flotante? Por supuesto, siempre puedes usar la representación binaria para obtener la precisión exacta con poco espacio, pero bueno ... –

+0

@ChristianRau: mi idea era desafiar el requisito inicial de que para que dos programas intercambien números en coma flotante se requeriría una representación literal. Dado que el formato está normalizado (y ordenado por el Estándar), el uso de la representación binaria es portátil (en el problema de endianness potencial). –

2

Si tiene una biblioteca C que se ajusta a C99 (y si sus tipos flotantes tienen una base que es una potencia de 2 :) el printf carácter de formato %a puede imprimir valores de coma flotante sin falta de precisión en forma hexadecimal, y las utilidades como scanf y strod podrán leerlas.

1

Si lee estos documentos (ver a continuación), encontrará que hay algunos algoritmos que imprimen el número mínimo de dígitos decimales de manera que el número puede ser reinterpretado sin cambios (es decir, mediante scanf).

Como puede haber varios de estos números, el algoritmo también escoge la fracción decimal más cercana a la fracción binaria original (nombré el valor de flotación).

Una pena que no hay tal biblioteca estándar en C.

Cuestiones relacionadas