Dans MATLAB, les variables sont-elles réellement à double précision par défaut?

Cette question est née de quelque chose d’étrange que j’ai remarqué après avoir étudié cette question plus avant …

J’ai toujours compris que les variables MATLAB étaient à double précision par défaut. Donc, si je devais faire quelque chose comme déclarer une variable avec 20 chiffres après le point décimal:

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

Je m’attendrais à ignorer les 4 derniers chiffres, car la précision relative en virgule flottante est de l’ordre de 10 -16 :

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

Si j’essaie d’afficher le numéro avec plus de 16 chiffres après le point décimal (en utilisant soit fprintf ou sprintf ), j’obtiens ce que j’attends de 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 étranges quand je passe num à la fonction arithmétique de précision variable dans la boîte à outils symbolique , lui indiquant de représenter le nombre en utilisant 21 chiffres de précision:

 >> vpa(num, 21) ans = 2.71828182845904553488 

QUELLE?! Ces 4 derniers chiffres ont réapparu! Ne devraient-ils pas être perdus lorsque le numéro original que j’ai entré était stocké sous la forme d’une variable à double précision num ? Comme num est une variable double précision lorsqu’elle est transmise à vpa , comment vpa at- vpa su ce qu’il était?

Ma meilleure estimation quant à 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 la virgule 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 source supplémentaire de confusion si vous n’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!!! 

Ils sont doubles. vpa() choisit simplement d’afficher des chiffres non significatifs au-delà de la précision relative en virgule flottante, où printf() et disp() tronquent ou les suppriment.

Vous ne récupérez que vos quatre chiffres d’origine, car le littéral que vous avez choisi d’initialiser avec num simplement l’expansion décimale exacte d’une valeur binary double, car elle a été copiée et collée à partir de la sortie de l’extension d’un double réel. valeur de l’autre question. Cela ne fonctionnera pas pour d’autres valeurs proches, comme vous le montrez dans votre addenda “BONUS”.

Plus précisément, tous les littéraux numériques de Matlab produisent des valeurs de type double. Ils sont convertis en valeur double binary la plus proche de la valeur décimale qu’ils représentent. En effet, les chiffres dans 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’a fait l’affiche de l’autre question avec e = ... , vous initialisez une valeur à partir d’un littéral, au lieu de traiter directement avec le résultat d’un précédent. expression.

Les différences ne concernent que le formatage de sortie. Je pense que ce qui se passe est que vpa() prend cette double précision binary double et la traite comme une valeur exacte. Pour une valeur donnée de mantisse-exposant binary, vous pouvez calculer l’équivalent décimal pour arbitrairement de nombreuses décimales. Si vous avez une précision limitée (“width”) dans la valeur binary, comme vous le faites avec n’importe quel type de données de taille fixe, seulement un grand nombre de ces chiffres décimaux sont significatifs. L’affichage par défaut de printf() et de Matlab gère cela en tronquant la sortie ou en affichant des chiffres non significatifs comme 0. vpa() ignore les limites de précision et continue à calculer autant de décimales que vous le demandez.

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 valeur binary double.

Voici un moyen de le montrer. Ces valeurs de x sont toutes identiques lorsqu’elles sont stockées dans des doubles et seront toutes représentées de la même manière 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 très proches les uns des autres.

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

vpa(x0) et vpa(x1) devraient produire des sorties très différentes du 16ème chiffre. Cependant, vous ne devriez pas pouvoir créer une valeur double x telle que vpa(x) produise une représentation décimale comprise entre vpa(x0) et vpa(x1) .

(UPDATE: Amro souligne que vous pouvez utiliser fprintf('%bx\n', x) pour afficher une représentation exacte de la valeur binary sous-jacente au format hexadécimal. Vous pouvez l’utiliser pour confirmer la carte littérale sur le même double.)

Je soupçonne que vpa() se comporte de cette manière car il traite ses entrées comme des valeurs exactes et supporte de manière polymorphe d’autres types de Matlab de la boîte à outils Symbolic qui ont plus de précision que les doubles. Ces valeurs devront être initialisées autrement que par des littéraux numériques, ce qui explique pourquoi sym() prend une chaîne en entrée et vpa(exp(1)) diffère de vpa(sym('exp(1)')) .

Avoir du sens? Désolé pour la longue convalescence.

(Remarque: je n’ai pas la boîte à outils symbolique, donc je ne peux pas tester vpa() moi-même.)

premier :

il semble que sprintf et fprintf ont un comportement différent sur les différentes versions de MATLAB, par exemple dans MATLAB 2018 a

 num=2.7182818284590666666666; sprintf('%0.70f', num) ans = '2.7182818284590668511668809514958411455154418945312500000000000000000000' 

seconde :

Nombres à virgule flottante

MATLAB® représente des nombres à virgule flottante en format double précision ou simple précision. La valeur par défaut est la double précision, mais vous pouvez utiliser n’importe quelle précision simple avec une fonction de conversion simple.

Virgule flottante double précision

MATLAB construit le type de données double précision (ou double) selon IEEE® Standard 754 pour une double précision. Toute valeur stockée en double nécessite 64 bits, formatés comme indiqué dans le tableau ci-dessous:

Bits: 63
Utilisation: signe (0 = positif, 1 = négatif)

Bits: 62 à 52 Utilisation: Exposant, biaisé par 1023

Bits: 51 à 0 Utilisation: Fraction f du nombre 1.f

se référer à ce lien pour plus d’infos

Entre 252 = 4,503,599,627,370,496 et 253 = 9,007,199,254,740,992, les nombres représentables sont exactement les nombres entiers. Pour la plage suivante, de 253 à 254, tout est multiplié par 2, donc les nombres représentables sont les nombres pairs, etc. Inversement, pour la plage précédente de 2 ^ 51 à 2 ^ 52, l’espacement est de 0,5, etc.

L’espacement comme fraction des nombres compris entre 2 ^ n et 2 ^ n + 1 est 2 ^ n-52. L’erreur d’arrondi relative maximale lors de l’arrondi d’un nombre à celui le plus proche représentable (la machine epsilon) est donc 2 ^ −53.

donc dans votre cas où n = 1 (2 ^ 1 <= num <= 2 ^ 2) l’espacement est 2 ^ -51,

Je pense qu’il est prudent de supposer que les algorithmes sprintf et sprintf pour afficher les nombres sont délicats et que le type MATLAB Double est basé sur la norme IEEE,


à propos de l’APV:

vpa utilise les chiffres de garde pour maintenir la précision

La valeur de la fonction des chiffres spécifie le nombre minimum de chiffres significatifs utilisés. En interne, vpa peut utiliser plus de chiffres que ne l’indiquent les chiffres. Ces chiffres supplémentaires sont appelés chiffres de garde car ils permettent d’éviter les erreurs d’arrondi dans les calculs ultérieurs.

Numériser approximativement 1/3 en utilisant quatre chiffres significatifs.

 a = vpa(1/3, 4) a = 0.3333 

Approximer le résultat a en utilisant 20 chiffres. Le résultat montre que la boîte à outils utilise en interne plus de quatre chiffres lors du calcul de a. Les derniers chiffres du résultat sont incorrects en raison de l’erreur d’arrondi.

 vpa(a, 20) ans = 0.33333333333303016843 

le problème que vous pouvez rencontrer est dû à l’espacement, à l’algorithme des chiffres de gaurd et au problème d’arrondi,

par exemple, en utilisant matlab 2018 a:

  sprintf('%0.28f', 8.0) ans = '8.0000000000000000000000000000' 

mais:

 sprintf('%0.28f', 8.1) ans = '8.0999999999999996447286321199' 

comme le nombre est compris entre 2 ^ 3 et 2 ^ 4, l’espacement est compris entre 2 ^ -49 (= 1,77 e-15), le nombre est donc valable jusqu’à la 15e décimale et

 sprintf('%0.28f', 64.1) ans = '64.0999999999999943156581139192' 

parce que le nombre est compris entre 2 ^ 6 et 2 ^ 7, donc l’espacement est 2 ^ -46 (= 1,42 e-14), donc le nombre est valable jusqu’à la 14ème décimale