matlab et c diffèrent avec la fonction cos

J’ai un programme implémenté dans matlab et le même programme dans c, et les résultats diffèrent.

Je suis un peu perplexe que la fonction cos ne renvoie pas exactement le même résultat.

J’utilise le même ordinateur, Intel Core 2 Duo et un double type de données sur 8 octets dans les deux cas.

Pourquoi le résultat est-il différent?

Voici le test:

c: double a = 2.89308776595231886830; double b = cos(a); printf("a = %.50f\n", a); printf("b = %.50f\n", b); printf("sizeof(a): %ld\n", sizeof(a)); printf("sizeof(b): %ld\n", sizeof(b)); a = 2.89308776595231886830106304842047393321990966796875 b = -0.96928123535654842068964853751822374761104583740234 sizeof(a): 8 sizeof(b): 8 matlab: a = 2.89308776595231886830 b = cos(a); fprintf('a = %.50f\n', a); fprintf('b = %.50f\n', b); whos('a') whos('b') a = 2.89308776595231886830106304842047393321990966796875 b = -0.96928123535654830966734607500256970524787902832031 Name Size Bytes Class Atsortingbutes a 1x1 8 double Name Size Bytes Class Atsortingbutes b 1x1 8 double So, b differ a bit (very slightly, but enough to make my debuging task difficult) b = -0.96928123535654842068964853751822374761104583740234 c b = -0.96928123535654830966734607500256970524787902832031 matlab 

J’utilise le même ordinateur, Intel Core 2 Duo et un double type de données sur 8 octets.

Pourquoi le résultat est-il différent?

Matlab n’utilise-t-il pas le matériel de la fonction cos intégré à Intel?

Existe-t-il un moyen simple d’utiliser la même fonction cos dans matlab et c (avec des résultats exacts), même un peu plus lentement, afin de pouvoir comparer en toute sécurité les résultats de mon programme matlab et c?


Mettre à jour:

merci beaucoup pour vos réponses!

Ainsi, comme vous l’avez souligné, la fonction cos pour matlab et c diffère. C’est incroyable! Je pensais qu’ils utilisaient la fonction cos intégrée au microprocesseur Intel.

La version cos de matlab est égale (au moins pour ce test) à celle de matlab. vous pouvez aussi essayer de matlab: b = java.lang.Math.cos (a)

Ensuite, j’ai fait une petite fonction MEX pour utiliser la version cos c de matlab, et cela fonctionne bien; Cela me permet de déboguer le mon programme (le même implémenté dans matlab et c) et de voir à quel point ils diffèrent, ce qui était le but de cet article.

Le seul problème, c’est que le fait d’appeler la version MEX c cos depuis Matlab est beaucoup trop lent.

J’essaie maintenant d’appeler la fonction Java Cos depuis c (comme c’est le même avec Matlab), voyez si cela va plus vite.

Utilisation du script à l’ adresse http://www.mathworks.com/matlabcentral/fileexchange/1777-from-double-to-ssortingng

la différence entre les deux nombres est seulement dans le dernier bit:

 octave:1> bc = -0.96928123535654842068964853751822374761104583740234; octave:2> bm = -0.96928123535654830966734607500256970524787902832031; octave:3> num2bin(bc) ans = -.11111000001000101101000010100110011110111001110001011*2^+0 octave:4> num2bin(bm) ans = -.11111000001000101101000010100110011110111001110001010*2^+0 

L’un d’eux doit être plus proche de la réponse “correcte”, en supposant que la valeur donnée pour a est exacte.

 >> be = vpa('cos(2.89308776595231886830)',50) be = -.96928123535654836529707365425580405084360377470583 >> bc = -0.96928123535654842068964853751822374761104583740234; >> bm = -0.96928123535654830966734607500256970524787902832031; >> abs(bc-be) ans = .5539257488326242e-16 >> abs(bm-be) ans = .5562972757925323e-16 

Ainsi, le résultat de la bibliothèque C est plus précis.

Toutefois, pour les besoins de votre question, vous ne devez pas vous attendre à obtenir la même réponse dans matlab et dans la bibliothèque C avec laquelle vous avez établi un lien.

Les nombres en virgule flottante sont stockés en binary et non en décimal. Un flottant double précision a double précision de 52 bits, ce qui correspond à environ 15 décimales significatives. En d’autres termes, les 15 premiers chiffres décimaux non nuls d’un double imprimé en décimal suffisent à déterminer de manière unique quel double été imprimé.

En tant que rationnel diadique , un double a une représentation exacte en décimal, ce qui prend bien plus de décimales que 15 pour représenter (dans votre cas, 52 ou 53 places, je crois). Cependant, les normes pour printf et les fonctions similaires ne nécessitent pas l’exactitude des chiffres après le 15; ils pourraient être un non-sens complet. Je soupçonne que l’un des deux environnements affiche la valeur exacte et l’autre une approximation médiocre, qui correspond en réalité à la même valeur double binary.

Le résultat est le même jusqu’à 15 décimales. Je suppose que cela suffit pour presque toutes les applications et si vous en avez besoin de plus, vous devriez probablement mettre en oeuvre votre propre version de cosinus, de sorte que vous maîsortingsiez les détails et que votre code soit portable. à travers différents compilateurs C.

Ils différeront parce qu’ils utilisent sans aucun doute différentes méthodes pour calculer l’approximation du résultat ou itèrent un nombre de fois différent. Comme le cosinus est défini comme une série infinie de termes, une approximation doit être utilisée pour sa mise en œuvre logicielle. L’algorithme CORDIC est une implémentation commune.

Malheureusement, je ne connais pas les détails de l’implémentation dans les deux cas. En effet, la version C dépend de l’implémentation de la bibliothèque standard C que vous utilisez.

Comme d’autres l’ont expliqué, lorsque vous entrez ce nombre directement dans votre code source, tous les fractions de chiffres ne seront pas utilisées, car vous n’obtenez que 15/16 décimales pour plus de précision. En fait, ils sont convertis en double valeur binary la plus proche (tout ce qui dépasse la limite fixée de chiffres est supprimé).

Pour aggraver les choses, et selon @R , IEEE 754 tolère les erreurs du dernier bit lors de l’utilisation de la fonction cosinus. En fait, j’ai rencontré ce problème lorsque j’utilise différents compilateurs.

Pour illustrer mon propos, j’ai testé avec le fichier MEX suivant, une fois compilé avec le compilateur LCC par défaut, puis avec VS2010 (je suis sous WinXP 32 bits).

Dans une fonction, nous appelons directement les fonctions C ( mexPrintf est simplement une macro #define as printf ). Dans l’autre, nous appelons mexEvalSsortingng pour évaluer les éléments du moteur MATLAB (équivalent à l’utilisation de l’invite de commande dans MATLAB).

prec.c

 #include  #include  #include  #include "mex.h" void c_test() { double a = 2.89308776595231886830L; double b = cos(a); mexPrintf("[C] a = %.25Lf (%16Lx)\n", a, a); mexPrintf("[C] b = %.25Lf (%16Lx)\n", b, b); } void matlab_test() { mexEvalSsortingng("a = 2.89308776595231886830;"); mexEvalSsortingng("b = cos(a);"); mexEvalSsortingng("fprintf('[M] a = %.25f (%bx)\\n', a, a)"); mexEvalSsortingng("fprintf('[M] b = %.25f (%bx)\\n', b, b)"); } void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { matlab_test(); c_test(); } 

copmiled avec LCC

 >> prec [M] a = 2.8930877659523189000000000 (4007250b32d9c886) [M] b = -0.9692812353565483100000000 (bfef045a14cf738a) [C] a = 2.8930877659523189000000000 ( 32d9c886) [C] b = -0.9692812353565484200000000 ( 14cf738b) <--- 

compilé avec VS2010

 >> prec [M] a = 2.8930877659523189000000000 (4007250b32d9c886) [M] b = -0.9692812353565483100000000 (bfef045a14cf738a) [C] a = 2.8930877659523189000000000 ( 32d9c886) [C] b = -0.9692812353565483100000000 ( 14cf738a) <--- 

Je comstack ce qui précède à l’aide de: mex -v -largeArrayDims prec.c et mex -v -largeArrayDims prec.c entre les compilateurs mex -setup avec: mex -setup

Notez que j'ai également essayé d'imprimer la représentation hexadécimale des nombres. J'ai seulement réussi à montrer la moitié inférieure des doubles nombres binarys en C (peut-être que vous pouvez obtenir l'autre moitié en utilisant des manipulations de bits, mais je ne suis pas sûr de savoir comment!)

Enfin, si vous avez besoin de plus de précision dans vos calculs, envisagez d'utiliser une bibliothèque pour l'arithmétique à précision variable. Dans MATLAB, si vous avez access à la Symbolic Math Toolbox , essayez:

 >> a = sym('2.89308776595231886830'); >> b = cos(a); >> vpa(b,25) ans = -0.9692812353565483652970737 

Vous pouvez donc voir que la valeur réelle se situe quelque part entre les deux approximations différentes que j'ai obtenues ci-dessus et qu'elles sont toutes égales jusqu'à la 15ème décimale:

 -0.96928123535654831.. # 0xbfef045a14cf738a -0.96928123535654836.. # <--- actual value (cannot be represented in 64-bit) -0.96928123535654842.. # 0xbfef045a14cf738b ^ 15th digit --/ 

METTRE À JOUR:

Si vous souhaitez afficher correctement la représentation hexadécimale des nombres en virgule flottante en C, utilisez plutôt cette fonction d'assistance (similaire à la fonction NUM2HEX dans MATLAB):

 /* you need to adjust for double/float datatypes, big/little endianness */ void num2hex(double x) { unsigned char *p = (unsigned char *) &x; int i; for(i=sizeof(double)-1; i>=0; i--) { printf("%02x", p[i]); } }