¿Existe un printf
especificador 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 float
con una precisión de 2
decimales:
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.9375
valor de punto flotante original (en este ejemplo, probablemente no lo haré).
Me gustaría una forma de indicar printf
que 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.h
para 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 sedouble
vuelve 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 losfloat
s /double
s 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.0
resultados en el valor a continuación. También se muestran losdouble
números de coma flotante representables anteriores y siguientes .Imprimir la representación decimal exacta de a
double
tiene 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- 1
está en orden. Nota: Esto-1
no 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 + 6
ver 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
%f
es 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.h
que defineFLT_DECIMAL_DIG
yDBL_DECIMAL_DIG
.fuente
%g
especificador?double
valores justo por encima de0.1
:1.000_0000_0000_0000_2e-01
,1.000_0000_0000_0000_3e-01
necesidad 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
%a
formato. 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
float
ydouble
es variable y depende del valor real defloat
odouble
.Se recuerdan
float
ydouble
se 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,
float
puede distinguir fácilmente entre 0.0 y 0.1.Pero
float
no tiene idea de la diferencia entre1e27
y1e27 + 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
%.f
modificador 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 .printf
no puedo / no puede manejar eso por ti.fuente
float
proporciona, y usted afirma que no existe tal cosa (es decir, que no existeFLT_DIG
), lo cual es incorrecto.FLT_DIG
que 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
.%e
imprime dígitos significativos para números muy pequeños y%f
no lo hace. ejx = 1e-100
.%.5f
impresiones0.00000
(una pérdida total de precesión).%.5e
impresiones1.00000e-100
.FLT_DIG
se define con el valor al que se define por una razón. Si es 6, es porquefloat
no 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.%.6f
no es equivalente, porqueFLT_DIG
no 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_DIG
sí conserva exactamente la representación binaria del número. Resultó que para los compiladores y bibliotecas de C que probé,DBL_DECIMAL_DIG
es 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.c
escrito 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