IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Turbo Pascal Discussion :

Factorielle : runtime error 200 [TPW]


Sujet :

Turbo Pascal

  1. #21
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 942
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 942
    Points : 5 654
    Points
    5 654
    Par défaut
    Gie,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function factorielle(n:integer):integer;
     
    var f,i: integer;
    begin
     for i:=1 to (n-1) do
      f:=f * n;
     factorielle := f;
    end;
    Ta fonction factorielle ne calcule pas du tout une factorielle, et en plus, elle utilise une variable locale non initialisée.

    Du coup, je n'ai pas lu plus loin.
    Si les cons volaient, il ferait nuit à midi.

  2. #22
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    31
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 31
    Points : 14
    Points
    14
    Par défaut
    Ahh desolé j'ai mis le code que je n'avais pas encore revus..desolé ! je vais edité mon poste.
    EDIT : voila j'ai edité mon code dans le poste precedant.

  3. #23
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    Citation Envoyé par AmineDrX Voir le message
    Ahh desolé j'ai mis le code que je n'avais pas encore revus..desolé ! je vais edité mon poste.
    EDIT : voila j'ai edité mon code dans le poste precedant.
    ça avance...

    - il est possible de supprimer le premier calcul de "s" en modifiant l'ordre des instructions dans le repeat/until

    - mais que viens donc faire "j" là dedans ? pourquoi lui donner la valeur 5 ?!

    - k est intéressant mais ne devrait-il pas changer de valeur de temps à autre ?
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  4. #24
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 942
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 942
    Points : 5 654
    Points
    5 654
    Par défaut
    Joa,
    Citation Envoyé par AmineDrX Voir le message
    Ahh desolé j'ai mis le code que je n'avais pas encore revus..desolé ! je vais edité mon poste.
    EDIT : voila j'ai edité mon code dans le poste precedant.
    Bien.

    Ça ressemble de plus en plus au code fait au coup par coup, sans vraie réflexion sur le problème, je n'interviendrai donc plus.
    Si les cons volaient, il ferait nuit à midi.

  5. #25
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    31
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 31
    Points : 14
    Points
    14
    Par défaut
    Citation Envoyé par droggo Voir le message
    Joa,

    Bien.

    Ça ressemble de plus en plus au code fait au coup par coup, sans vraie réflexion sur le problème, je n'interviendrai donc plus.
    J'ai copier/coller le code qui était dans l'autre fenêtre du TPW.
    Si tu penses que c'est un code fait au coup par coup, c'est ton problème.
    Bref,
    Citation Envoyé par Paul TOTH Voir le message
    ça avance...

    - il est possible de supprimer le premier calcul de "s" en modifiant l'ordre des instructions dans le repeat/until

    - mais que viens donc faire "j" là dedans ? pourquoi lui donner la valeur 5 ?!

    - k est intéressant mais ne devrait-il pas changer de valeur de temps à autre ?
    En faite, j'ai utilisé le j=5 car j'avais besoin d'une variable impaire comme c'est indiqué dans l'exercice, & j'avais supprimé le test if (j mod 2) <> 0 then..

    Pour le k, je voulais qu'il soit a un moment positif puis negatif a un autre moment, je me suis un peu embrouillé !

    Finalement je viens de revoir toute cette partie, j'ai essayé de corriger petit a petit & trouver des solutions, mais je me suis retrouvé a changer la façon avec la quelle je l'avais écrite.

    J'ai supprimé ma 3éme fonction somme, que j'ai réécrite dans le programme principale, ce qui a éliminer quelques problèmes pendant l’exécution, puis je me suis attaqué a ma 2éme fonction puissance, que j'ai réécrite en procédure.
    & j'avais un résultat, qui me faisait doutait, du coup j'ai pris ma calculatrice pour calculer manuellement la série donné dans l’énoncé de mon exercice en donnant des valeurs a x aux hasards, j'ai compris ensuite que le problème venait de la limite s<=0.0001, je l'ai changé en 0.1 j'ai exécuter avec 4 variables aux hasards puis j'ai comparé les résultats a ceux calculé manuellement avec ma calculatrice & ça collait.
    Si il y a une remarque n’hésitez pas biensur, je ne suis qu'un débutant en L1.
    Voilà mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    program exo5;
    uses wincrt;
     
    var x,n,j,pu: integer; sinx,terme:real;
     
     
    procedure saisie(var x:integer);
    begin
     
     repeat
      clrscr;
      writeln('calcul de sinus(x) ');
      write('donner une valeur a x : ');
      readln(x);
     until (x<=400);
    end;
     
     
    function factorielle(n:integer):integer;
     
    var f,i: integer;
    begin
     f:=1;
     for i:=2 to n do
      f:=f * i;
     factorielle := f;
    end;
     
     
    procedure puissance(x,n: integer; var pu:integer);
     
    var i:integer;
    begin
     pu:=x;
     for i:=1 to (n-1) do
      pu:= pu*x;
    end;
     
     
    begin
     
    saisie(x);
    sinx:=0;
    j:=1;
    terme:=x;
     
    while (terme>0.1) do
     begin
      if (((j div 2) mod 2)= 0) then
        sinx:=sinx+terme
      else
        sinx:=sinx-terme;
     
      j:=j+2;
      n:=j;
      puissance(x,n,pu);
      terme:=pu/factorielle(j);
     end;
     
     writeln('sin(',x,' = ',sinx);
     
    end.

  6. #26
    Membre confirmé
    Homme Profil pro
    .
    Inscrit en
    Juin 2002
    Messages
    239
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : .
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2002
    Messages : 239
    Points : 567
    Points
    567
    Par défaut
    Bonjour.

    Faire du calcul numérique n'est déjà pas évident en soi, le faire avec l'outil informatique est particulièrement difficile.
    Pourquoi ?
    Parce que l'ordinateur est intrinsèquement limité, à la différence de l'esprit humain.
    Il nous est possible d'imaginer des nombres entiers extrêmement grands ( par exemple 1000000! ) ou des nombres réels avec une précision arbitraire ( par exemple le nombre Pi avec 100 000 000 000 de décimales ), mais cela est difficile à mettre en oeuvre avec un ordinateur car un ordinateur n'a qu'un nombre fini d'états possibles.

    Avant de se lancer dans un calcul sur ordinateur, il faut bien connaître les types de données que l'on va utiliser et leur limitations.
    Sans cette connaissance, le programme risquera de donner un résultat faux, même s'il est algorithmiquement correct.

    Sous Turbo-Pascal, un integer est stocké sur deux octets ; il est donc limité à 2^16 -1 = 65535.
    Tout ce qui dépasse cette valeur sera coupé, et la variable correspondante ne contiendra pas le nombre attendu.
    Par exemple, 8! = 40320 peut être stocké dans un integer, mais pas 9! = 362880.
    Sous Turbo-Pascal, un longint est stocké sur quatre octets ; il est donc limité à 2^32 -1 = 4294967295.
    Ainsi 12! = 479001600 pourra être stocké dans un longint, mais pas 13! = 6227020800.

    On pourrait penser que l'on n'aura pas à aller si loin et que le programme n'utilisera que de petites valeurs de n avant de s'arrêter.
    C'est une erreur !
    La suite u_n = x^n/n! converge vers 0 mais, si x est grand, elle commence par prendre de grandes valeurs avant de revenir vers 0.
    Son maximum en valeur absolue est atteint lorsque n est égal à la partie entière de x, et cette valeur absolue vaut environ e^x.
    Si x vaut 100, la suite u_n va croître jusqu'à 10^44 environ avant de revenir vers 0.
    Et elle ne sera inférieure à 0.0001 qu'a partir de n = 277.
    Or 277! vaut environ 10^558.
    Il est donc impossible dans ce cas de stocker n! dans un integer ou dans un longint.

    Le type real est-il une solution ?
    Et bien non, car un real est limité à 10^38.
    Par contre le type extended va jusqu'à 10^4932.
    Mais attention : il ne s'agit pas d'entiers mais de nombres réels ; leur valeur n'est pas exacte mais arrondie et on ne dispose que d'un nombre limité de chiffres significatifs.
    Néanmoins, pour un début, on peut faire avec ...
    Il faut donc revoir la définition de la procédure factorielle : elle doit retourner un extended.

    Ensuite, il faut revoir aussi celle de la procédure puissance.
    Curieusement, la version de cette procédure donnée au début du post était correcte, mais la dernière est complètement fausse : n est bien un integer, mais x et pu ne le sont pas !
    Il faut déclarer x en real et pu en extended, y compris au début du programme.

    Commençons par ces modifications, je donnerai d'autres conseils quand cette première version tournera correctement.

  7. #27
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    31
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 31
    Points : 14
    Points
    14
    Par défaut
    Bonsoir,
    J'avais lus à propos des differents des types de variables sur http://cyberzoide.developpez.com/info/turbo/chap4.php3, mais il me manquait tout de meme quelques precisions, merci donc pour ces explications & conseils

    Par contre j'ai une question, si la fonction factorielle renvois un extended donc je modifie aussi le type de ma variable f ? car .

    En compilant mon programme aprés avoir apporter les modifications que vous avez indiqué, j'ai une erreur qui s'affiche : Coprocesseur 80x87 obligatoire pour compiler ceci.
    Cette erreur je l'ai eu avant aujourd'hui, quand je pensais que le problème venait des types que j'ai mis a mes fonctions, j'avais essayé de mettre Extended puis Double a mes fonctions, & ça me sortait cette erreur.

    EDIT: dois-je modifier aussi le type de mes deux variable sinx & terme ?
    car elles sont declarés de types Real, & a la fin de mon programme j'ai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     terme:=pu/factorielle(j);
    & : Comme un Extended est plus grand qu'un Real, devraient-elles renvoyés aussi un Extended ?

    Voila le code aprés modification :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    program exo5;
    uses wincrt;
     
    var n,j: integer; pu:extended; sinx,terme,x:real;
     
     
    procedure saisie(var x:integer);
    begin
     
     repeat
      clrscr;
      writeln('calcul de sinus(x) ');
      write('donner une valeur a x : ');
      readln(x);
     until (x<=400);
    end;
     
     
    function factorielle(n:integer):extended;
     
    var i: integer; f:extended;
    begin
     f:=1;
     for i:=2 to n do
      f:=f * i;
     factorielle := f;
    end;
     
     
    procedure puissance(n: integer; x:real; var pu:extended);
     
    var i:integer;
    begin
     pu:=x;
     for i:=1 to (n-1) do
      pu:= pu*x;
    end;
     
     
    begin
     saisie(x);
     sinx:=0;
     j:=1;
     terme:=x;
     while (terme>0.1) do
      begin
       if (((j div 2) mod 2)= 0) then
        sinx:=sinx+terme
       else
        sinx:=sinx-terme;
        j:=j+2;
        n:=j;
        puissance(x,n,pu);
        terme:=pu/factorielle(j);
       end;
     
     writeln('sin(x) = ',sinx);
    end.

  8. #28
    Membre émérite

    Homme Profil pro
    Formation: Chimie et Physique (structure de la matière)
    Inscrit en
    Décembre 2010
    Messages
    1 333
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Formation: Chimie et Physique (structure de la matière)
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 333
    Points : 2 570
    Points
    2 570
    Billets dans le blog
    9
    Par défaut [TPW] Factorielle : runtime error 200
    Bonjour,

    Il me semble que le débat a un peu dévié compte tenu du nombre de difficultés à résoudre, malgré la mise au point très claire de Prof sur l'opportunité du type EXTENDED pour le calcul des fonctions exponentielles et factorielles.

    Lorsque l'on doit calculer une somme S(n) = U0 + U1 + ... + Un
    dont chaque terme est le rapport de deux grandeurs rapidement croissantes en valeur absolue: Uk = Vk/Wk , avec dans le cas présent:
    Vk = [(-1)^k]*x^(2k+1) et Wk = (2k+1)!
    il est préférable d'éviter le calcul séparé des termes (Vk, Wk), et de s'en tenir à celui des quotients successifs (Uk) par une relation de récurrence:
    R(k) = U(k+1)/U(k) = -x^2/(2k+2)(2k+3) ;
    on voit au passage que R(k)=-1 pour x compris entre (2k+2) et (2k+3), ce qui permet de localiser facilement le plus grand des termes |Uk|.

    Par ailleurs, l'énoncé ne donne apparemment pas le domaine des valeurs de (x); en l'absence d'une borne supérieure, il me paraît difficile d'exprimer la condition d'arrêt du calcul:
    x^n/n! <= 0.0001
    Peut-être indique-t-on quelque part: 0 <= x <= Pi ?
    Cela conduirait à k(max) = E(0.341) = 0 et simplifirait le problème, la décroissance de |Uk| commançant à partir du premier terme U1.


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  9. #29
    Membre confirmé
    Homme Profil pro
    .
    Inscrit en
    Juin 2002
    Messages
    239
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : .
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2002
    Messages : 239
    Points : 567
    Points
    567
    Par défaut
    Bonjour,

    La réponse à la première question est : OUI
    A partir du moment où la fonction factorielle est déclarée comme retournant une valeur extended, la variable f qui sert à la calculer doit aussi être déclarée en extended.

    La réponse à la deuxième question est également : OUI
    Comme la variable terme peut prendre de grandes valeurs, il faut la déclarer en extended.
    Par voie de conséquence, la variable sinx doit aussi être déclarée en extended.
    Par contre la variable x peut être déclarée en real, Turbo-Pascal faisant automatiquement un trans-typage lors des affectations comme " terme:=x; " ou " pu:= pu*x; ".

    Quant au message " Coprocesseur 80x87 obligatoire pour compiler ceci ", il me parait curieux ...
    A partir du 486, le coprocesseur mathématique a été incorporé au microprocesseur et c'est le cas pour tous les Pentium.
    Le microprocesseur de votre ordinateur est-il un vieux 386 non accompagné du coprocesseur 387 ?
    Cela parait impossible ...
    Allez voir dans Turbo-Pascal, Options, Compiler.
    Dans la fenêtre qui s'ouvre, il y a une zone " Numeric processing " avec deux lignes et deux cases à cocher.
    Il faut cocher la première ligne "8087/80287", qui indique à Turbo-Pascal que le coprocesseur est présent.
    Notez que sans coprocesseur, on peut utiliser le type extended en demandant à Turbo-Pascal de faire les calculs en émulation logicielle.
    Il suffit de placer au début du programme la directive : {$N+,E+}

    Lorsque Turbo-Pascal pourra compiler le programme, il signalera les erreurs suivantes :
    1) la variable x de la procédure saisie doit être un real, non un integer.
    2) l'ordre des variables x et n a été interverti entre la définition de la procédure puissance et son appel.

    Notez que, lorsque ces deux erreurs auront été corrigées, le programme tournera mais il sera encore faux ...

    L'emploi de la variable j est en effet incorrect :
    Au début, la variable j vaut 1.
    Le premier test de parité retourne donc faux et l'instruction " sinx:=sinx-terme; " est exécutée.
    Comme terme vaut x, sinx vaut à ce stade -x, ce qui est l'opposé de ce l'on voulait ...
    D'autre part, j est incrémenté à chaque fois de 2 : il restera donc impair, ce qui n'est pas non plus ce que l'on voulait ...

    Afin que j change de parité à chaque fois, il faut l'incrémenter de 1 et non de 2.
    Par contre n doit être incrémenté de 2.
    Conclusion : j et n doivent être deux variables indépendantes.
    La variable n doit être initialisée à 1, puis incrémentée à chaque fois de 2.
    Quant à la variable j, elle doit être initialisée à 1, puis changée de signe à chaque fois.
    En effet, plutôt que faire un test de parité, il est plus simple de faire à chaque fois :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     sinx := sinx + j*terme;
     j:=-j;
    D'ailleurs, pour la clarté du code, il est préférable de changer le nom de j et de l'appeler signe ...

    Il faudra enfin remettre la précision à 0.0001, comme demandé dans l'énoncé de l'exercice.

    wiwaxia : je suis tout à fait d'accord avec votre explication sur le calcul des sommes partielles de séries.
    Mais, AmineDrX ayant expliqué qu'il était un débutant, je souhaitais que l'on arrive à faire tourner son programme avant de lui proposer une autre approche .

  10. #30
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    31
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 31
    Points : 14
    Points
    14
    Par défaut
    Bonsoir,
    Désolé je n'ai pas pus répondre plus tôt, je suis en plein période d'examen

    Citation Envoyé par Prof Voir le message
    Bonjour,

    La réponse à la première question est : OUI
    A partir du moment où la fonction factorielle est déclarée comme retournant une valeur extended, la variable f qui sert à la calculer doit aussi être déclarée en extended.

    La réponse à la deuxième question est également : OUI
    Comme la variable terme peut prendre de grandes valeurs, il faut la déclarer en extended.
    Par voie de conséquence, la variable sinx doit aussi être déclarée en extended.
    Par contre la variable x peut être déclarée en real, Turbo-Pascal faisant automatiquement un trans-typage lors des affectations comme " terme:=x; " ou " pu:= pu*x; ".

    Quant au message " Coprocesseur 80x87 obligatoire pour compiler ceci ", il me parait curieux ...
    A partir du 486, le coprocesseur mathématique a été incorporé au microprocesseur et c'est le cas pour tous les Pentium.
    Le microprocesseur de votre ordinateur est-il un vieux 386 non accompagné du coprocesseur 387 ?
    Cela parait impossible ...
    Allez voir dans Turbo-Pascal, Options, Compiler.
    Dans la fenêtre qui s'ouvre, il y a une zone " Numeric processing " avec deux lignes et deux cases à cocher.
    Il faut cocher la première ligne "8087/80287", qui indique à Turbo-Pascal que le coprocesseur est présent.
    Notez que sans coprocesseur, on peut utiliser le type extended en demandant à Turbo-Pascal de faire les calculs en émulation logicielle.
    Il suffit de placer au début du programme la directive : {$N+,E+}
    C'est fait concernant l'erreur de coprocesseur, désormais elle ne s'affiche plus, merci.
    Lorsque Turbo-Pascal pourra compiler le programme, il signalera les erreurs suivantes :
    1) la variable x de la procédure saisie doit être un real, non un integer.
    2) l'ordre des variables x et n a été interverti entre la définition de la procédure puissance et son appel.

    Notez que, lorsque ces deux erreurs auront été corrigées, le programme tournera mais il sera encore faux ...
    Effectivement, j'ai remarqué l'erreur de type & l'ordre des variables dans les parametres de ma procedure, donc je viens de corriger ma bourde.

    L'emploi de la variable j est en effet incorrect :
    Au début, la variable j vaut 1.
    Le premier test de parité retourne donc faux et l'instruction " sinx:=sinx-terme; " est exécutée.
    Comme terme vaut x, sinx vaut à ce stade -x, ce qui est l'opposé de ce l'on voulait ...
    D'autre part, j est incrémenté à chaque fois de 2 : il restera donc impair, ce qui n'est pas non plus ce que l'on voulait ...

    Afin que j change de parité à chaque fois, il faut l'incrémenter de 1 et non de 2.

    Par contre n doit être incrémenté de 2.
    Conclusion : j et n doivent être deux variables indépendantes.
    La variable n doit être initialisée à 1, puis incrémentée à chaque fois de 2.
    Quant à la variable j, elle doit être initialisée à 1, puis changée de signe à chaque fois.
    En effet, plutôt que faire un test de parité, il est plus simple de faire à chaque fois :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     sinx := sinx + j*terme;
     j:=-j;
    D'ailleurs, pour la clarté du code, il est préférable de changer le nom de j et de l'appeler signe ...
    En effet, je me suis compliqué les choses, en ayant mal utilisé la variable j, je l'avais initialisé a 1 puis incrémenté a chaque fois de 2, pour qu'elle reste impaire, puis j'affectais sa valeur a n, qui elle doit être impair, & tout ça en oubliant que j devait me servir a changé le signe de la variable terme.

    Il faudra enfin remettre la précision à 0.0001, comme demandé dans l'énoncé de l'exercice.
    C'est fait.
    Par contre, je n'arrive pas à comprendre comment mon programme donnait des résultats identiques à ceux trouvé manuellement en utilisant la série de calcul donné dans l'énoncé ?


    Voila mon code modifié, mais les resultats donnés ne sont pas juste :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    program exo5;
    uses wincrt;
     
    var n,signe: integer; pu:extended; sinx,terme,x:real;
     
     
    procedure saisie(var x:real);
    begin
     
     repeat
      clrscr;
      writeln('calcul de sinus(x) ');
      write('donner une valeur a x : ');
      readln(x);
     until (x<=400);
    end;
     
     
    function factorielle(n:integer):extended;
     
    var i: integer; f:extended;
    begin
     f:=1;
     for i:=2 to n do
      f:=f * i;
     factorielle := f;
    end;
     
     
    procedure puissance(n: integer; x:real; var pu:extended);
     
    var i:integer;
    begin
     pu:=x;
     for i:=1 to (n-1) do
      pu:= pu*x;
    end;
     
     
    begin
     
     saisie(x);
     sinx:=0;
     signe:=1;
     n:=1;
     terme:=x;
     
     while (terme>0.0001) do
      begin
        sinx:=sinx+ signe*terme;
        signe:=-signe;
        n:=n+2;
        puissance(n,x,pu);
        terme:=pu/factorielle(n);
       end;
     
     writeln('sin(x) = ',sinx);
    end.

  11. #31
    Membre confirmé
    Homme Profil pro
    .
    Inscrit en
    Juin 2002
    Messages
    239
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : .
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2002
    Messages : 239
    Points : 567
    Points
    567
    Par défaut
    Bonjour.

    On y est presque ...

    Il y a encore un détail qui cloche, mais le reste est algorithmiquement correct.
    Ce détail, c'est le test d'arrêt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     while (terme>0.0001) do
    Si x est positif, alors terme est aussi positif et le test est correct.
    Par contre, si x est négatif, la puissance pu change alternativement de signe et donc terme aussi.
    Si, par exemple, terme vaut -2, alors le test d'arrêt va s'appliquer bien que terme ne soit pas proche de 0.
    L'erreur se trouve en fait dans l'énoncé de l'exercice : ce n'est pas x^n/n! <= 0.0001 qu'il faut tester mais |x^n/n!| <= 0.0001.
    C'est-à-dire : la valeur absolue de x^n/n! inférieure à 0.0001.
    Il faut donc remplacer le test d'arrêt par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     while (abs(terme)>0.0001) do
    Maintenant, le code est algorithmiquement correct, mais il est encore informatiquement incorrect.
    C'est-à-dire que, sur le papier, l'algorithme utilisé est juste mais qu'un ordinateur utilisant cet algorithme ne donnera pas forcément un résultat correct.
    Pourquoi ?
    Pour la raison que j'ai expliquée dans mon premier post, à savoir qu'un ordinateur est intrinsèquement limité.

    Si on fait tourner ce programme en entrant de petites valeurs pour x, le résultat affiché est correct.
    Par contre, si on entre la valeur 100 pour x, Turbo-Pascal stoppe l'exécution et affiche :
    Error 205: Floating point overflow
    L'erreur se produit au niveau du calcul de terme, ligne 55.
    A ce stade, pu et factorielle(n) sont deux très grands nombres et leur quotient ne rentre pas dans terme qui est un real.
    Comme je l'avais expliqué dans le premier post :
    " Si x vaut 100, la suite u_n va croître jusqu'à 10^44 environ avant de revenir vers 0. "
    Or un real est limité à 10^38 ...
    Il faut donc déclarer terme et sinx en extended et non pas en real.
    Seul x peut rester en real.

    Notez que cela supprime l'erreur d'exécution, mais que le résultat affiché par Turbo-Pascal lorsque x vaut 100 est faux.
    Il affiche en effet : sin(x) = -1.06959157620746E+0023.
    Cela fait beaucoup pour le sinus d'un nombre réel ...

    On touche ici aux difficultés profondes du calcul numérique sur ordinateur.
    Les variables de type réel ne contiennent pas la valeur exacte d'un nombre mais une valeur approchée avec un nombre limité de chiffres significatifs.
    Une variable de type extended peut stocker un réel jusqu'à 10^4932, avec 20 chiffres significatifs.
    C'est beaucoup, mais c'est insuffisant ici.
    Si x vaut 100, terme monte jusqu'à 10^44 avant de redescendre vers 0.
    La variable terme, déclarée en extended, peut contenir sans problème une telle valeur, mais sous forme approchée : seuls les 20 premiers chiffres ( ceux du haut ) sont stockés, les autres sont ignorés.
    Comme 44 - 20 = 24, cela entraîne que la partie basse de la valeur, celle qui va de 0 à 10^24, est ignorée.
    Cela se répercute aussitôt sur sinx : la partie basse de sa valeur est ignorée.
    Cela signifie que la valeur de sinx comprise entre 0 et 10^24, est devenue arbritraire, sans aucune signification.
    Cela ne serait pas grave si sinx restait un nombre grand, mais en fait, par suite des simplifications successives, sa partie haute va petit à petit être réduite et il devrait revenir entre - 1 et 1.
    Mais comme cette zone a été écrasée par les grands nombres utilisés dans les calculs intermédiaires, la valeur finale de sinx est totalement fausse.

    Heureusement, il y a deux solutions pour résoudre ce problème.

    La première consiste à remarquer que la fonction sinus est périodique, de période 2*Pi.
    Il est donc inutile de calculer sin(x) pour de grandes valeurs de x.
    Il suffit de le faire pour une valeur comprise entre 0 et 2*Pi.

    La première chose à faire, lorsque x a été saisi par l'utilisateur, est donc de remplacer x par x -2*k*Pi, où k est un entier judicieux.
    ( si k est un entier, les deux nombres x et x -2*k*Pi ont le même sinus )
    Quant à l'entier k judicieux, c'est celui tel que x -2*k*Pi soit compris entre 0 et 2*Pi.
    C'est donc la partie entière de x/(2*Pi).
    Sachant que sous Turbo-Pascal la fonction partie entière est notée Int, la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     x := x -2*Pi*Int(x/2/Pi) ;
    est à placer juste après la saisie de x.

    Ainsi, la valeur x utilisée dans les calculs sera toujours inférieure à 2*Pi, et terme restera inférieur à 86.
    La valeur finale de sinx sera donc connue avec 20 - 2 = 18 chiffres significatifs ( si on ne tient pas compte des erreurs intermédiaires ), ce qui correspond à nos besoins.
    Attention : c'est sinx que l'on obtient avec 18 chiffres significatifs, pas le sinus de x.
    En effet, la série calculant sin(x) n'a pas été calculée en entier ( c'est évidemment impossible ).
    On s'est arrêté à un entier n tel que abs(terme) <= 0.0001 et le théorème spécial des séries alternées nous affirme que sinx est proche de sin(x) à 0.0001 près.

    Accessoirement, l'entier n restera inférieur à 22 et le programme tournera donc plus vite, même si la différence est difficile à percevoir.

    La deuxième solution a été évoquée plus haut par wiwaxia.
    J'y reviendrait dans le post suivant.

  12. #32
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    Citation Envoyé par Prof Voir le message
    Bonjour.
    Bonjour est merci pour ces explications très claires sur les "à côté" de la programmation
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  13. #33
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    31
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 31
    Points : 14
    Points
    14
    Par défaut
    Bonsoir,
    Je pense avoir bien suivis vos explications, ce qui nous donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
     
    program exo5;
    uses wincrt;
     
    var n,signe: integer; pu,sinx,terme:extended; x:real;
     
     
    procedure saisie(var x:real);
    begin
     
     repeat
      clrscr;
      writeln('calcul de sinus(x) ');
      write('donner une valeur a x : ');
      readln(x);
     until (x<=400);
    end;
     
     
    function factorielle(n:integer):extended;
     
    var i: integer; f:extended;
    begin
     f:=1;
     for i:=2 to n do
      f:=f * i;
     factorielle := f;
    end;
     
     
    procedure puissance(n: integer; x:real; var pu:extended);
     
    var i:integer;
    begin
     pu:=x;
     for i:=1 to (n-1) do
      pu:= pu*x;
    end;
     
     
    begin
     
     saisie(x);
     x := x -2*Pi*Int(x/(2*Pi)) ;
     sinx:=0;
     signe:=1;
     n:=1;
     terme:=x;
     
     while (abs(terme)>0.0001) do
      begin
        sinx:=sinx+ signe*terme;
        signe:=-signe;
        n:=n+2;
        puissance(n,x,pu);
        terme:=pu/factorielle(n);
       end;
     
     writeln('sin(x) = ',sinx);
    end.
    J'attend de lire la deuxiemme solution, car soit je n'ai pas bien suivis la premiere, soit le calcul reste toujours tres compliqué a faire pour l'ordinateur. Je dis ça car le resultat renvoyé par le programme est toujours faux.

  14. #34
    Membre confirmé
    Homme Profil pro
    .
    Inscrit en
    Juin 2002
    Messages
    239
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : .
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2002
    Messages : 239
    Points : 567
    Points
    567
    Par défaut
    Bonjour.

    Ce programme fonctionne parfaitement !

    Il donne le sinus de x avec une précision de 0.0001, lorsque x est en radians et non en degrés.

    Maintenant que l'algorithme et l'implémentation sur ordinateur sont corrects, on peut considérer que l'exercice est résolu.

    Néanmoins il existe un méthode plus efficace pour calculer ce type de sommes, celle qui avait été proposée par wiwaxia plus haut dans la discussion.

    Plutôt que de calculer u(n) de manière absolue ( en calculant directement x^n et n! puis en faisant leur quotient ), il est préférable de calculer u(n) de manière relative, c'est-à-dire à partir de u(n-1).

    Si u(n) vaut x^n/n!, alors le rapport u(n)/u(n-1) vaut x/n et ainsi u(n) = u(n-1)x/n.
    Informatiquement, si le terme u(n) est stocké dans la variable u, cela s'écrit u := u*x/n.

    En fait, pour le calcul de sinus, le terme général de la série est u(n) = (-1)^n x^(2n+1)/(2n+1)!, car seules les puissances impaires apparaissent et que le signe change alternativement.
    Dans ce cas, le rapport u(n)/u(n-1) vaut - x^2/(2n)(2n+1) et ainsi u(n) = -u(n-1)x^2/(2n)(2n+1).
    Informatiquement, cela s'écrit u := -u*x*x/2/n/(2*n+1), sachant que u devra être initialisé à x car u(0) = x.

    Finalement, voici le programme que je propose :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    Program Sinus ;
     
    var  n : integer ;
         x, u, S : real ;
     
    procedure saisie(var x : real) ;
    begin
         writeln('calcul de sinus') ;
         write('valeur de x : ') ;
         readln(x) ;
    end;
     
    Begin        
         saisie(x) ;
         x := x - 2*Pi*Int(x/2/Pi) ;
         n := 0 ;
         u := x ;
         S := x ;
         repeat
               n := n+1 ;
               u := -u*x*x/2/n/(2*n+1) ;
               S := S + u ;
         until abs(u) < 0.0001 ;
         writeln('sin(x)= ',S:10:8) ;
    end.
    J'ai noté u le terme général de la série et S la somme partielle, en accord avec les règles usuelles en mathématiques.
    Et j'ai initialisé S à x, le premier terme de la série.
    C'est plus simple pour comprendre le code quand on est habitué à ce type de calcul.
    D'autre part, j'ai remplacé la boucle " while ... do ... " par " repeat ... until ... ".
    Affaire de goût personnel : la condition de sortie de la boucle se situe à la fin de la boucle, je trouve que c'est plus clair ainsi.

  15. #35
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    31
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 31
    Points : 14
    Points
    14
    Par défaut
    Bonjour,
    Oui le programme fonctionne parfaitement !
    Le programme que tu proposes est nettement mieux
    Merci a Paul TOTH pour son aide,& biensur merci Prof, je pense que tout ceux qui ont lus ce sujet ont profités de vos bonne explications

    Bonne journée & a la prochaine

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. [BPW] Runtime error 200 (division par 0)
    Par dzeus dans le forum Turbo Pascal
    Réponses: 7
    Dernier message: 23/05/2007, 11h05
  2. [TP] Runtime error 200 : programme TP5 non patchable
    Par HERON Daniel dans le forum Turbo Pascal
    Réponses: 11
    Dernier message: 02/11/2005, 21h53
  3. [TP]Runtime error 106 à l'exécution
    Par BlackTiger dans le forum Turbo Pascal
    Réponses: 2
    Dernier message: 25/01/2004, 21h50
  4. [LG]runtime error 202
    Par picsou123 dans le forum Langage
    Réponses: 2
    Dernier message: 14/11/2003, 22h53
  5. [Kylix] Runtime error 230 avec INDY
    Par Anonymous dans le forum EDI
    Réponses: 2
    Dernier message: 23/03/2002, 11h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo