2010-11-19 21 views
52

Esta pregunta surgió de algo extraño que me di cuenta después de investigar this question más ...En MATLAB, ¿las variables REALMENTE son de doble precisión por defecto?

siempre entendí variables de MATLAB para ser double-precision por defecto. Así que, 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 

Yo esperaría que los últimos 4 dígitos para ser ignorado, ya que el floating-point relative accuracy 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), que consiga lo que esperaba 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 extrañas cuando paso num a la variable precision arithmetic function en el Symbolic Toolbox, diciéndole que para representar el número con 21 dígitos de precisión:

>> vpa(num,21) 
ans = 
2.71828182845904553488 

¿QUÉ? ¡Han vuelto a aparecer los últimos 4 dígitos! ¿No deberían haberse perdido cuando el número original que ingresé se almacenó como una variable de precisión doble num? Dado que num es una variable de precisión doble cuando se pasa a vpa, ¿cómo sabe vpa cuáles fueron?

Mi mejor estimación sobre 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?



PRIMA: Y aquí es una fuente adicional de confusión si aún no dispone de una migraña de lo anterior ...

>> 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!!! 

Respuesta

51

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.

Usted 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 valor doble real 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 simplemente 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 demostrarlo. 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) debe producir resultados que difieren mucho más allá de la 16a dígitos. 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 se puede utilizar fprintf('%bx\n', x) para mostrar una representación exacta del valor binario subyacente en formato hexadecimal Usted puede usar esto para confirmar los literales se asignan a la misma doble)

Sospecho 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)')) ".

¿Tiene sentido? Perdón por el alboroto.

(Nota no tengo la caja de herramientas Simbólico así que no puedo probar VPA() a mí mismo.)

+1

1 de respuesta instructivo una pregunta interesante! – Jonas

+1

Aha! Entonces, casualmente estaba usando la expansión decimal exacta de un valor binario como mi número de prueba. ¡Todo tiene sentido ahora! No estoy seguro de cómo me las arreglé para perder eso, aunque ... tal vez fue debido a la falta de sueño ya que mi hija está saliendo y me mantuvo despierta toda la noche. ;) – gnovice

+1

@Andrew: ¿estás trabajando en MathWorks? – Mikhail

Cuestiones relacionadas