Bizarrerie sur des doubles
Alain
Membre
Bonjour,
Tout nouvau en Objective-c, j'ai commencé par la lectures des doc Apple et surtout par leur tutoriel "Currency Converter"
(pour memoire : deux champs "taux de change" et "Montant à changer", un champ "Resultat" et un bouton pour lancer la conversion
Tout a fonctionné jusqu'au moment où je l'ai utilisé: taux = 1,2; montant = 1,3; résultat = 1,560000061988831; Ce qui ne me semble pas satisfaisant.
En examinant les variable je vois 1,2 devient 1,20000005; 1,3 devient 1,29999995; le produit 1,56000006 devient ce qui est affiché.
Je constate donc une "erreur" lors du passage de l'affichage à la valeur utilisée et vice-versa.
Pouvez-vous me dire comment cela se fait, et comment cela peut être corrigé
Merci
Alain
Tout nouvau en Objective-c, j'ai commencé par la lectures des doc Apple et surtout par leur tutoriel "Currency Converter"
(pour memoire : deux champs "taux de change" et "Montant à changer", un champ "Resultat" et un bouton pour lancer la conversion
Tout a fonctionné jusqu'au moment où je l'ai utilisé: taux = 1,2; montant = 1,3; résultat = 1,560000061988831; Ce qui ne me semble pas satisfaisant.
En examinant les variable je vois 1,2 devient 1,20000005; 1,3 devient 1,29999995; le produit 1,56000006 devient ce qui est affiché.
Je constate donc une "erreur" lors du passage de l'affichage à la valeur utilisée et vice-versa.
Pouvez-vous me dire comment cela se fait, et comment cela peut être corrigé
Merci
Alain
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Pour que ça t'affiche un résultat plus propre il faut que tu utilises un NSNumberFormatter que tu configures pour arrondir les nombres et avoir quelque chose de plus satisfaisant.
À part ça, bienvenu sur Objective-Cocoa.org.
Test: Quand on fait tous les tests (100.*a,bc == abc )? pour a, b, c des chiffres de 0 à 9, il y a 6,9% d'erreurs
et c'est normal !
Là , tu dois déclarer ta variable en float et tenter de l'écrire avec une précision trop importante:
  float x=1.2;
  printf("%.8f",x);
La précision d'un float n'est pas de 8 chiffres après la virgule.
Tu n'as pas cette erreur en double, il faut utiliser plus de chiffres après la virgule pour provoquer une erreur semblable.
Ceci vient du fait que l'écriture en binaire de 1/5 demande une infinité de chiffres : 5 n'est pas une puissance de 2.
what every computer scientist should know about floating-point arithmetic est une référence pour toutes les explications relatives à l'encodage des nombres à virgule, et des problèmes qui vont avec.
+
Chacha
D'ailleurs sur ce blog y'a 2 ou 3 autres articles intéressants (même s'il est plus trop mis à jour), genre sur comment Cocoa optimise les classes de types collection et array, ou les memory-barrier dans les applis multi-cores ou multi-threading, ...
J'ai pas eu le temps de tout lire, mais l'article que tu proposes a l'air vraiment excellent. Merci beaucoup
#include <stdio.h>
#include <math.h>
#define MAX(a,b) ( (a)>(b) ? (a) : (b) )
int main(void){
double a=-0.999950034599999982 , b=1.01000000000000001;
double c=-1.01005054,d=1.0;
double m11=100,m12=101,m21=99,m22=100;
printf( "a = c à %.0f %% près\n",fabs((c-a)/a)*100.);
printf( "b = d à %.0f %% près\n",fabs((d-b)/b)*100.);
printf("Calcul de [m11,m12;m21,m22]*[a;b] = [%.4f,%.4f]\n",m11*a+m12*b,m21*a+m22*b);
printf("Calcul de [m11,m12;m21,m22]*[c;d] = [%.4f,%.4f]\n",m11*c+m12*d,m21*c+m22*d);
printf("Soit un rapport abscisse/abscisse ou ordonnée/ordonnée = %.0f %%\n",
100.*MAX((m11*a+m12*b)/(m11*c+m12*d) , (m21*a+m22*b)/(m21*c+m22*d)));
return 0;
}
% pgm
a = c à 1 % près
b = d à 1 % près
Calcul de [m11,m12;m21,m22]*[a;b] = [2.0150,2.0049]
Calcul de [m11,m12;m21,m22]*[c;d] = [-0.0051,0.0050]
Soit un rapport abscisse/abscisse ou ordonnée/ordonnée = 40127 %
%
Embêtant , non ?
Des exemples comme celui-là , on peut en pondre autant qu'on en veut !
Donc après j'imagine que tu as aussi choisi un exemple exprès de matrice mal conditionnée (||M|| * ||M^-1|| très grand --> résultat de l'application de M sur un vecteur quelconque très sensible aux perturbations/erreur sur ce vecteur) pour mettre en avant et grossir le problème. Mais ici ça n'est pas alors forcément dû aux floats (ou du moins pas que) mais aussi et surtout au choix de ta matrice.
En général si on est amené à faire des calculs matriciels par exemple justement on prend soin de prendre des matrices correctement conditionnées... C'est pareil pour les rotation, c'est de là que sont aussi nés les quaternions pour exprimer les rotations plutôt que de passer par des matrices de transformations lassiques d'ailleurs : d'une part pour rajouter la notion de sens de la rotation (+90 et -270° n'a pas le même sens) mais aussi pour éviter les problèmes de précision et d'inversion de polarité lorsqu'on tourne trop près de l'axe, etc...
Conclusion : dans ce genre de domaine faut toujours avoir à l'esprit la prise en compte de l'erreur dans les calculs... et calculer cette erreur aussi ou s'assurer de son impact.
Je vois que tu te souviens de tes cours, cela fait plaisir au vieux prof de math que je suis.
Il s'agit effectivement d'une matrice mal conditionnée, et les résultats obtenus ne sont pas les résultats d'erreurs de calculs, mais la description de la réalité.
Mais au vu de la matrice
  [ 100 101 ]
  [ 99  100]
qui se doute qu'il y a un piège dans cette matrice ?
Qui se doute que faire une approximation à deux chiffres après la virgule peut nuire grave ?
La présentation du conditionnement dans la littérature par les normes ||M|| * ||M^-1|| très grand est une complication intellectuelle qui rend obscur un phénomène tout simple de valeurs propres :
On a ici une matrice M admettant deux vecteurs u et v réalisant Mu~200u et Mv~0.005v. En combinant u et v on provoque une distorsion dans les calculs, qui n'est pas une erreur.
En informatique graphique, les matrices utilisées sont des matrices de rotation , de symétrie, et d'échelle (scaleXBy: yBy;), et elles ne présentent pas ce genre de pathologie (sauf à le faire exprès pour le matrices d'échelle). Â
Donc confirme-moi, c'est bien le fait que tes VP de ta matrice sont assez éloignées l'une de l'autre (l'une très grande par rapport à l'autre) que ta matrice est mal conditionnée ? Car justement les composantes principales de ta matrice sont dans un rapport élevé l'une par rapport à l'autre ?
(Et donc que du coup la moindre petite erreur d'approximation peut avoir en effet de l'influence...)
Ah ça fait drôle de ressortir tout ça, ça rappelle des souvenirs
Voici un petit programme pour écrire les deux plus petits double positifs
% pgm
Le plus petit double positif est 0.500000 2^-1073
Le suivant est 0.500000 2^-1072
%
#include <stdio.h>
#include <math.h>
/* Quelques fonctions d'exploration de math.h
Comparer
fmin, fmax, fdim:Â fdim(x,y) = max{x-y,0}.
fabs, copysign: copysign(x,y) attribue à x le signe de y.
nextafter, nexttoward: nextafter(x,y) est le réel suivant x dans la direction de y.
Les fonctions liées à l'écriture exposant-mantisse
modf, logb, ilogb, frexp: Décompose x en x' * 2^n.
ldexp, scalbn, scalbln:Â calcul de x'*2^n.
Comme de coutume, on peut ajouter les mêmes fonctions avec le suffixe l pour les long double, et f pour les float
*/
int main(void){
double x=nextafter(0.0,1.0);
int exponent;
double y=frexp(x,&exponent);
printf( "Le plus petit double positif est %f 2^%d\n",y,exponent);
x=nextafter(x,1.0);
y=frexp(x,&exponent);
printf( "Le suivant est %f 2^%d\n",y,exponent);
return 0;
}
C'est exactement cela, le quotient des valeurs propres est très grand par rapport à 1, et cela rend la matrice mal conditionnée.
Pour une rotation ou une symétrie ce quotient est de 1, c'est-à -dire l'idéal et ce genre de situations ne se produira pas.
Pour une matrice d'échelle, cela ne se produira que si on fait exprès de prendre le quotient (rapport en x/rapport en y) très grand (ou très petit).
Et malgré ces problèmes de math, restons philosophe mon cher Alain. :P
Sur la base du programme quelques posts plus haut, voici la situation sur les premiers flottants positifs :
+++++++++++++++++ Pour l'exposant -1073 , on trouve 1 flottants
+++++++++++++++++ Pour l'exposant -1072 , on trouve 1 flottants
+++++++++++++++++ Pour l'exposant -1071 , on trouve 2 flottants
+++++++++++++++++ Pour l'exposant -1070 , on trouve 4 flottants
+++++++++++++++++ Pour l'exposant -1069 , on trouve 8 flottants
+++++++++++++++++ Pour l'exposant -1068 , on trouve 16 flottants
+++++++++++++++++ Pour l'exposant -1067 , on trouve 32 flottants
+++++++++++++++++ Pour l'exposant -1066 , on trouve 64 flottants
+++++++++++++++++ Pour l'exposant -1065 , on trouve 128 flottants
+++++++++++++++++ Pour l'exposant -1064 , on trouve 256 flottants
+++++++++++++++++ Pour l'exposant -1063 , on trouve 512 flottants
sans surprise ...
Et malgré grâce à ces problèmes de math, restons philosophe mon cher Alain. :P
Merci de toutes vos réponses. Je dois avouer que certaines m'ont rappelé quelques vieux cours de math, qui ont malgré tout eu quel mal à remonter à la surface.. et je reste philosophe.
Je me doutais bien qu'il y a là deux effets de bord : l'un lié à la conversion de texte en réel (et vice-versa), l'autre aux erreurs de multiplication. Et je concevais qu'un simple arrondi à deux ou trois décimales (voire quatre ou cinq) résolvait le pb...
Et j'ai eu la réponse : les "formateurs".
Tout est donc bien.
Merci