xlabel sprintf comando variables matlab floating-point precision

sprintf - En MATLAB, ¿las variables REALMENTE son de doble precisión por defecto?



title matlab (1)

Esta pregunta surgió de algo extraño que noté después de investigar más esta cuestión ...

Siempre entendí que las variables de MATLAB son de double-precision por defecto. Entonces, si tuviera que hacer algo como declarar una variable con 20 dígitos después del punto decimal:

>> num = 2.71828182845904553488; >> class(num) %# Display the variable type ans = double

Esperaría que se ignoren los últimos 4 dígitos, ya que la precisión relativa de coma flotante es del orden de 10 -16 :

>> eps(num) ans = 4.440892098500626e-016

Si trato de mostrar el número con más de 16 dígitos después del punto decimal (usando FPRINTF o SPRINTF ), obtengo lo que espero ver:

>> fprintf(''%0.20f/n'',num) 2.71828182845904550000 >> sprintf(''%0.20f'',num) ans = 2.71828182845904550000

En otras palabras, los dígitos 17 a 20 son todos 0.

Pero las cosas se ponen raras cuando paso num a la función aritmética de precisión variable en la caja de herramientas simbólica , diciéndole que represente el número usando 21 dígitos de precisión:

>> vpa(num,21) ans = 2.71828182845904553488

¡¿QUÉ?! Esos últimos 4 dígitos han reaparecido! ¿No deberían haberse perdido cuando el número original que ingresé se almacenó como un num variable de precisión doble? Como num es una variable de precisión doble cuando se pasa a vpa , ¿cómo supo vpa lo que era?

Mi mejor conjetura en cuanto a lo que está sucediendo es que MATLAB representa internamente num con más precisión que un doble ya que lo inicialicé a un número con más dígitos más allá del punto decimal que una variable de precisión doble podría manejar. ¿Es esto realmente lo que está sucediendo, o está sucediendo algo más?


BONUS: Y aquí hay una fuente adicional de confusión si aún no tienes una migraña de los anteriores ...

>> num = 2.71828182845904553488; %# Declare with 20 digits past the decimal >> num = 2.718281828459045531; %# Re-declare with 18 digits past the decimal >> vpa(num,21) ans = 2.71828182845904553488 %# It''s the original 20-digit number!!!


Ellos son dobles. Vpa () simplemente elige mostrar dígitos no significativos más allá de la precisión relativa del punto flotante, donde printf () y disp () truncan o ponen a cero.

Solo está recuperando sus cuatro dígitos originales porque el literal que eligió para inicializar num con simplemente sucede que es la expansión decimal exacta de un valor doble binario, porque fue copiado y pegado de la salida de la expansión de un doble real valor de la otra pregunta. No funcionará para otros valores cercanos, como lo muestra en su apéndice "BONIFICACIÓN".

Más precisamente, todos los literales numéricos en Matlab producen valores de tipo double. Se convierten al valor doble binario que está más cerca del valor decimal que representan. En efecto, los dígitos en un literal más allá del límite de precisión del tipo doble se eliminan en silencio. Cuando copia y pega el resultado de vpa para crear una nueva variable, como hizo el póster de la otra pregunta con la instrucción "e = ...", está inicializando un valor de un literal, en lugar de tratar directamente con el resultado de una expresión previa.

Las diferencias aquí están solo en el formato de salida. Creo que lo que está sucediendo es que vpa () toma ese doble binario de doble precisión y lo trata como un valor exacto. Para un valor binario de exponente de mantisa dado, puede calcular el decimal equivalente arbitrariamente en muchos lugares decimales. Si tiene una precisión limitada ("ancho") en el valor binario, como lo hace con cualquier tipo de datos de tamaño fijo, solo muchos de esos dígitos decimales son significativos. Sprintf () y la pantalla predeterminada de Matlab manejan esto truncando la salida o mostrando dígitos no significativos como 0. Vpa () está ignorando los límites de precisión y continúa calculando tantos decimales como usted solicite.

Esos dígitos adicionales son falsos, en el sentido de que si fueran reemplazados por otros valores para producir un valor decimal cercano, todos serían "redondeados" al mismo valor doble binario.

Aquí hay una manera de mostrarlo. Estos valores de x son todos iguales cuando se almacenan en dobles, y todos serán representados por vpa ().

x = [ 2.7182818284590455348848081484902650117874145507812500 2.7182818284590455348848081484902650117874145507819999 2.7182818284590455348848 2.71828182845904553488485555555555555555555555555555 exp(1) ] unique(x)

Aquí hay otra forma de demostrarlo. Aquí hay dos dobles que están muy cerca el uno del otro.

x0 = exp(1) x1 = x0 + eps(x0)

Vpa (x0) y vpa (x1) deberían producir salidas que difieran mucho más allá del decimosexto dígito. Sin embargo, no debería poder crear un doble valor x tal que vpa (x) produzca una representación decimal que se encuentre entre vpa (x0) y vpa (x1).

(ACTUALIZACIÓN: Amro señala que puede usar fprintf(''%bx/n'', x) para mostrar una representación exacta del valor binario subyacente en formato hexadecimal. Puede usar esto para confirmar el mapa de literales al mismo doble).

Sospecho que vpa () se comporta de esta manera porque trata sus entradas como valores exactos y admite polimórficamente otros tipos de Matlab de la caja de herramientas simbólica que tienen más precisión que los dobles. Esos valores deberán inicializarse por medios distintos a los literales numéricos, por lo que sym () toma una cadena como entrada y "vpa (exp (1))" difiere de "vpa (sym (''exp (1)'') ) ".

¿Tener sentido? Perdón por el alboroto.

(Tenga en cuenta que no tengo la Caja de herramientas simbólica, así que no puedo probar vpa ()).