¿Alguien puede explicarme cómo funciona [.precision] en printf con el especificador "%g"? Estoy bastante confundido por el siguiente resultado:
double value = 3122.55; printf("%.16g\n", value); //output: 3122.55 printf("%.17g\n", value); //output: 3122.5500000000002 Aprendí que %g usa la representación más corta.
Pero las siguientes salidas todavía me confunden
printf("%.16e\n", value); //output: 3.1225500000000002e+03 printf("%.16f\n", value); //output: 3122.5500000000001819 printf("%.17e\n", value); //output: 3.12255000000000018e+03 printf("%.17f\n", value); //output: 3122.55000000000018190 Mi pregunta es: ¿por qué %.16g da el número exacto mientras que %.17g no puede?
Parece que 16 dígitos significativos pueden ser precisos. ¿Alguien podría decirme el motivo?
%g usa la representación más corta.
Los números de coma flotante generalmente no se almacenan como un número en base 10 , sino 2 (rendimiento, tamaño, razones prácticas). Sin embargo, cualquiera que sea la base de su representación, siempre habrá números racionales que no serán expresables en algún límite de tamaño arbitrario para que la variable los almacene.
Cuando especifica %.16g , está diciendo que desea la representación más corta del número dado con un máximo de 16 dígitos significativos.
Si la representación más corta tiene más de 16 dígitos, printf acortará la cadena de números cortando los 2 dígitos al final, dejándote con 3122.550000000000 , que en realidad es 3122.55 en la forma más corta, explicando el resultado que obtuviste.
En general, %g siempre le dará el resultado más corto posible, lo que significa que si la secuencia de dígitos que representa su número se puede acortar sin pérdida de precisión, se hará.
Para continuar con el ejemplo, cuando usa %.17g y el lugar decimal 17 contiene un valor diferente de 0 ( 2 en particular), termina con el número completo 3122.5500000000002 .
Mi pregunta es: ¿por qué
%.16gda el número exacto mientras que%.17gno puede?
En realidad, es el %.17g el que le da el resultado exacto, mientras que el %.16g le da solo un aproximado redondeado con un error (en comparación con el valor en la memoria).
Si desea una precisión más fija, utilice %f o %F en su lugar.
El valor decimal 3122.55 no se puede representar exactamente en punto flotante binario. Cuando escribes
double value = 3122.55; terminas con el valor más cercano posible que se puede representar exactamente. Da la casualidad de que ese valor es exactamente 3122.5500000000001818989403545856475830078125 .
Ese valor a 16 cifras significativas es 3122.550000000000 . A 17 cifras significativas, es 3122.5500000000002 . Y esas son las representaciones que te dan %.16g y %.17g .
Tenga en cuenta que se garantiza que la representación double más cercana de un número decimal tiene una precisión de al menos 15 cifras decimales significativas. Es por eso que necesita imprimir a 16 o 17 dígitos para comenzar a ver estas imprecisiones aparentes en su salida en este caso: a cualquier número menor de cifras significativas, se garantiza que la representación double coincida con el número decimal original que escribió.
Una nota final: dices que
Aprendí que
%gusa la representación más corta.
Si bien este es un resumen popular de cómo se comporta %g , también es incorrecto. Consulte ¿Qué significa exactamente el especificador %g printf? donde discuto esto extensamente y muestro un ejemplo de %g usando notación científica a pesar de que tiene 4 caracteres más de lo que habría sido sin usar notación científica.
La representación decimal 3122.55 no se puede representar exactamente mediante una representación de coma flotante binaria.
Un valor de coma flotante binaria de doble precisión puede representar aproximadamente 15 cifras significativas (tenga en cuenta que no son lugares decimales ) de un valor decimal correctamente; a partir de entonces, los dígitos pueden no ser los mismos, y en los extremos ni siquiera tienen un significado real y serán un artefacto de la conversión de la representación de punto flotante a una cadena de dígitos decimales.
Aprendí que %g usa la representación más corta.
La regla es:
Donde P es la precisión (o 6 si no se especifica precisión o 1 si la precisión es cero), y X es el exponente decimal requerido para la notación de estilo E/e, entonces:
La modificación de la precisión para %g da como resultado una salida diferente de:
printf("%.16g\n", value); //output: 3122.55 printf("%.16e\n", value); //output: 3.1225500000000002e+03 printf("%.16f\n", value); //output: 3122.5500000000001819a pesar de tener la misma precisión en el especificador de formato.