¿Existe un printfespecificador de ancho que se pueda aplicar a un especificador de punto flotante que formatee automáticamente la salida con el número necesario de dígitos significativos de modo que al escanear la cadena de nuevo, se adquiera el valor de punto flotante original?
Por ejemplo, supongamos que imprimo a floatcon una precisión de 2decimales:
float foobar = 0.9375;
printf("%.2f", foobar);    // prints out 0.94
Cuando escaneo la salida 0.94, no tengo ninguna garantía de cumplimiento de los estándares de que recuperaré el 0.9375valor de punto flotante original (en este ejemplo, probablemente no lo haré).
Me gustaría una forma de indicar printfque imprima automáticamente el valor de punto flotante con la cantidad necesaria de dígitos significativos para garantizar que se pueda escanear de nuevo al valor original que se le pasó printf.
Podría usar algunas de las macros float.hpara derivar el ancho máximo al que pasar printf, pero ¿ya existe un especificador para imprimir automáticamente con la cantidad necesaria de dígitos significativos , o al menos con el ancho máximo?
fuente

printf( "%f", val );que ya es portátil, eficiente y predeterminada.double. A medida que sedoublevuelve extremadamente grande (muy lejos de 1.0), en realidad se vuelve menos preciso en la parte decimal (parte del valor menor que 1.0). Así que realmente no se puede tener una respuesta satisfactoria aquí, porque su pregunta tiene una falsa suposición de que (a saber, que todos losfloats /doubles son iguales)Respuestas:
Recomiendo la solución hexadecimal @Jens Gustedt: use% a.
OP quiere "imprimir con la máxima precisión (o al menos hasta el decimal más significativo)".
Un ejemplo simple sería imprimir un séptimo como en:
Pero profundicemos ...
Matemáticamente, la respuesta es "0.142857 142857 142857 ...", pero estamos usando números de coma flotante de precisión finita. Supongamos que es binario de doble precisión IEEE 754 . Entonces los
OneSeventh = 1.0/7.0resultados en el valor a continuación. También se muestran losdoublenúmeros de coma flotante representables anteriores y siguientes .Imprimir la representación decimal exacta de a
doubletiene usos limitados.C tiene 2 familias de macros
<float.h>para ayudarnos.El primer conjunto es el número de dígitos significativos que se imprimirán en una cadena en decimal, por lo que al escanear la cadena, obtenemos el punto flotante original. Se muestran con el valor mínimo de la especificación C y un compilador C11 de muestra .
El segundo conjunto es el número de dígitos significativos que se puede escanear una cadena en un punto flotante y luego se imprime el FP, conservando la misma presentación de cadena. Se muestran con el valor mínimo de la especificación C y un compilador C11 de muestra . Creo que disponible pre-C99.
El primer conjunto de macros parece cumplir el objetivo de OP de dígitos significativos . Pero esa macro no siempre está disponible.
El "+ 3" fue el quid de mi respuesta anterior. Se centra en si conociendo la conversión de ida y vuelta string-FP-string (conjunto # 2 macros disponibles C89), ¿cómo se determinarían los dígitos para FP-string-FP (conjunto # 1 macros disponibles después de C89)? En general, agregar 3 fue el resultado.
Ahora, ¿cuántos dígitos significativos imprimir se conocen y se controlan
<float.h>?Para imprimir N dígitos decimales significativos, se pueden utilizar varios formatos.
Con
"%e", el campo de precisión es el número de dígitos después del dígito inicial y el punto decimal. Entonces- 1está en orden. Nota: Esto-1no se encuentra en laint Digs = DECIMAL_DIG;Con
"%f", el campo de precisión es el número de dígitos después del punto decimal. Para un número comoOneSeventh/1000000.0, sería necesarioOP_DBL_Digs + 6ver todos los dígitos significativos .Nota: muchos están acostumbrados a
"%f". Eso muestra 6 dígitos después del punto decimal; 6 es el valor predeterminado de la pantalla, no la precisión del número.fuente
%fes 6."%f"en primer lugar. Usar"%e"como mostró es, por supuesto, un mejor enfoque en general y efectivamente una respuesta decente (aunque quizás no sea tan bueno como"%a"podría serlo si está disponible, y por supuesto"%a"debería estar disponible si `DBL_DECIMAL_DIG lo está). Siempre he deseado un especificador de formato que siempre redondee exactamente a la precisión máxima (en lugar de los 6 lugares decimales codificados).La respuesta corta para imprimir números de punto flotante sin pérdidas (de modo que puedan leerse exactamente en el mismo número, excepto NaN e Infinity):
printf("%.9g", number).printf("%.17g", number).NO lo use
%f, ya que eso solo especifica cuántos dígitos significativos después del decimal y truncará los números pequeños. Como referencia, se pueden encontrar los números mágicos 9 y 17 en losfloat.hque defineFLT_DECIMAL_DIGyDBL_DECIMAL_DIG.fuente
%gespecificador?doublevalores justo por encima de0.1:1.000_0000_0000_0000_2e-01,1.000_0000_0000_0000_3e-01necesidad 17 dígitos de distinguir."%.16g"es insuficiente y"%.17g"y"%.16e"son suficientes. Los detalles de%g, fueron mal recordados por mí.Si solo está interesado en el bit (resp patrón hexadecimal), puede usar el
%aformato. Esto te garantiza:Debo agregar que esto solo está disponible desde C99.
fuente
No, no existe un especificador de ancho de printf para imprimir en punto flotante con la máxima precisión . Déjame explicarte por qué.
La precisión máxima de
floatydoublees variable y depende del valor real defloatodouble.Se recuerdan
floatydoublese almacenan en formato sign.exponent.mantissa . Esto significa que se utilizan muchos más bits para el componente fraccionario para números pequeños que para números grandes.Por ejemplo,
floatpuede distinguir fácilmente entre 0.0 y 0.1.Pero
floatno tiene idea de la diferencia entre1e27y1e27 + 0.1.Esto se debe a que toda la precisión (que está limitada por el número de bits de mantisa) se utiliza para la mayor parte del número, a la izquierda del decimal.
El
%.fmodificador solo dice cuántos valores decimales desea imprimir desde el número flotante en lo que respecta al formato . El hecho de que la precisión disponible dependa del tamaño del número depende de usted como programador .printfno puedo / no puede manejar eso por ti.fuente
floatproporciona, y usted afirma que no existe tal cosa (es decir, que no existeFLT_DIG), lo cual es incorrecto.FLT_DIGque no significa nada. Esta respuesta afirma que el número de lugares decimales disponibles depende del valor dentro del flotador .Simplemente use las macros de
<float.h>y el especificador de conversión de ancho variable (".*"):fuente
printf("%." FLT_DIG "f\n", f);%e, no tan bien para%f: solo si sabe que el valor para imprimir es cercano a1.0.%eimprime dígitos significativos para números muy pequeños y%fno lo hace. ejx = 1e-100.%.5fimpresiones0.00000(una pérdida total de precesión).%.5eimpresiones1.00000e-100.FLT_DIGse define con el valor al que se define por una razón. Si es 6, es porquefloatno puede contener más de 6 dígitos de precisión. Si lo imprime con%.7f, el último dígito no tendrá significado. Piense antes de votar en contra.%.6fno es equivalente, porqueFLT_DIGno siempre es 6. ¿Y a quién le importa la eficiencia? La E / S ya es cara como el infierno, un dígito con más o menos precisión no creará un cuello de botella.Realizo un pequeño experimento para verificar que la impresión con
DBL_DECIMAL_DIGsí conserva exactamente la representación binaria del número. Resultó que para los compiladores y bibliotecas de C que probé,DBL_DECIMAL_DIGes de hecho el número de dígitos requerido, e imprimir incluso con un dígito menos crea un problema significativo.Ejecuto esto con el compilador C de Microsoft 19.00.24215.1 y gcc versión 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1). El uso de un dígito decimal menos reduce a la mitad el número de números que se comparan exactamente iguales. (También verifiqué que
rand()tal como se usa produce alrededor de un millón de números diferentes). Aquí están los resultados detallados.Microsoft C
GCC
fuente
RAND_MAX == 32767. Considereu.s[j] = (rand() << 8) ^ rand();o similar para asegurarse de que todos los bits tengan la oportunidad de ser 0 o 1.En uno de mis comentarios a una respuesta, lamenté que durante mucho tiempo había querido alguna forma de imprimir todos los dígitos significativos en un valor de punto flotante en forma decimal, de la misma manera que la pregunta. Bueno, finalmente me senté y lo escribí. No es del todo perfecto, y este es un código de demostración que imprime información adicional, pero funciona principalmente para mis pruebas. Por favor, avíseme si usted (es decir, alguien) desea una copia de todo el programa contenedor que lo impulsa para probarlo.
fuente
Que yo sepa, no existe un algoritmo bien difusa que permite la salida al número necesario de dígitos significativos de tal manera que cuando se escanea la parte posterior de cadena en el valor de punto flotante original se adquirió en
dtoa.cescrito por Daniel Gay, que está disponible aquí en Netlib (véase también el papel asociado ). Este código se utiliza, por ejemplo, en Python, MySQL, Scilab y muchos otros.fuente