2010-11-19 30 views
52

Cette question a été posée sur quelque chose d'étrange que j'ai remarqué après avoir enquêté sur this question plus loin ...Dans MATLAB, les variables sont-elles VRAIMENT en double-précision par défaut?

J'ai toujours compris les variables Matlab être double-precision par défaut. Donc, si je devais faire quelque chose comme déclarer une variable avec 20 chiffres après la virgule:

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

j'attendre les 4 derniers chiffres à ignorer, puisque le floating-point relative accuracy est de l'ordre de 10 -16 :

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

Si je tente d'afficher le nombre avec plus de 16 chiffres après la virgule (en utilisant FPRINTF ou SPRINTF), je reçois ce que j'attends à voir:

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

En d'autres termes, les chiffres 17 à 20 sont tous 0.

Mais les choses deviennent bizarres quand je passe num au variable precision arithmetic function dans le Symbolic Toolbox, lui disant de représenter le nombre en utilisant 21 chiffres de précision:

>> vpa(num,21) 
ans = 
2.71828182845904553488 

QUOI ?! Les 4 derniers chiffres ont réapparu! Ne devraient-ils pas être perdus lorsque le numéro d'origine que j'ai entré a été enregistré comme variable à double précision num? Puisque num est une variable à double précision lorsqu'elle est passée à vpa, comment vpa a-t-elle été identifiée? Ma meilleure idée de ce qui se passe est que MATLAB représente en interne num avec plus de précision qu'un double puisque je l'ai initialisé à un nombre avec plus de chiffres après le point décimal qu'une variable à double précision pourrait gérer. Est-ce vraiment ce qui se passe, ou est-ce que quelque chose d'autre se passe?



BONUS: Et voici une autre source de confusion si vous ne l'avez pas déjà une migraine de ce qui précède ...

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

Répondre

51

Ils sont doubles. Vpa() choisit simplement d'afficher les chiffres non significatifs au-delà de la précision relative en virgule flottante, où printf() et disp() les tronquent ou les mettent à zéro.

Vous récupérez uniquement les quatre chiffres d'origine car le littéral que vous avez choisi d'initialiser num correspond à l'expansion décimale exacte d'une double valeur binaire, car il a été copié et collé à partir de la sortie de l'extension d'une double valeur réelle de l'autre question. Cela ne fonctionnera pas pour d'autres valeurs voisines, comme vous le montrez dans votre addenda "BONUS".

Plus précisément, tous les littéraux numériques dans Matlab produisent des valeurs de type double. Ils sont convertis en la double valeur binaire la plus proche de la valeur décimale qu'ils représentent. En effet, les chiffres d'un littéral au-delà de la limite de précision du type double sont supprimés en silence.Lorsque vous copiez et collez la sortie de vpa pour créer une nouvelle variable, comme l'affiche de l'autre question l'a fait avec l'instruction "e = ...", vous initialisez une valeur à partir d'un littéral, au lieu de traiter directement le résultat de une expression précédente.

Les différences ici sont juste dans le formatage de sortie. Je pense que ce qui se passe est que vpa() prend ce double binaire double précision et le traite comme une valeur exacte. Pour une valeur de mantisse-exposant binaire donnée, vous pouvez calculer l'équivalent décimal arbitrairement de nombreuses décimales. Si vous avez une précision limitée ("width") dans la valeur binaire, comme pour tout type de données de taille fixe, seul un grand nombre de ces chiffres décimaux sont significatifs. Sprintf() et l'affichage par défaut de Matlab gèrent cela en tronquant la sortie ou en affichant les chiffres non significatifs comme 0. Vpa() ignore les limites de précision et continue de calculer autant de décimales que vous le souhaitez. Ces chiffres supplémentaires sont faux, en ce sens que s'ils étaient remplacés par d'autres valeurs pour produire une valeur décimale proche, ils seraient tous "arrondis" à la même double valeur binaire.

Voici un moyen de le montrer. Ces valeurs de x sont toutes les mêmes lorsqu'elles sont stockées en double, et seront toutes représentées par vpa().

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

Voici une autre façon de le démontrer. Voici deux doubles qui sont très proches les uns des autres. Vpa (x0) et vpa (x1) devraient produire des sorties qui diffèrent beaucoup du 16ème chiffre. Cependant, vous ne devriez pas être capable de créer une double valeur x telle que vpa (x) produise une représentation décimale qui se situe entre vpa (x0) et vpa (x1).

(MISE À JOUR:.. Amro indique que vous pouvez utiliser fprintf('%bx\n', x) pour afficher une représentation exacte de la valeur binaire sous-jacente au format hexadécimal Vous pouvez l'utiliser pour confirmer les littéraux carte à la même deux)

Je soupçonne vpa() se comporte de cette façon car il traite ses entrées comme des valeurs exactes et prend en charge polymorphiquement d'autres types Matlab de la boîte à outils symbolique qui ont plus de précision que doubler. Ces valeurs devront être initialisées par des moyens autres que des littéraux numériques, c'est pourquoi sym() prend une chaîne en entrée et "vpa (exp (1))" diffère de "vpa (sym ('exp (1)')) ".

Avez-vous du sens? Désolé pour le long-ventedness.

(Note Je n'ai pas la boîte à outils symbolique, donc je ne peux pas tester VPA() moi-même.)

+1

+1 pour répondre à une question instructive intéressante! – Jonas

+1

Aha! Je viens donc d'utiliser l'expansion décimale exacte d'une valeur binaire comme mon numéro de test. Tout s'explique maintenant! Je ne sais pas comment j'ai réussi à manquer cela, peut-être que c'était dû au manque de sommeil depuis que ma fille fait ses dents et m'a gardé toute la nuit. ;) – gnovice

+1

@Andrew: travaillez-vous chez MathWorks? – Mikhail