Une calculatrice "en ligne"

Présentation

Images

CalculatriceCalculatriceCalculatrice

Description

Vous en avez marre de ces sources de calculette qui se contentent de vous offrir des boutons ?
Je comprends.
Cependant, il faut avouer que la réalisation d'une calculatrice capable de transformer le string :
(sqr(e(ln((1+3)*(1/(2^2))))))^2
en calcul n'est pas facile (pour les plus courageux, ca vaut 1 :-))!

Il faut bien penser que les maths ne nous simplifient pas le travail : priorité des opérations, parenthèses (le code 2(2+1) = 2*(2+1) n'est pas forcément évident !)...

Cette calculette semble bien s'en tirer...cependant, j'ai commencé à la programmer il y a 48h, il est donc possible que certains bugs se soient dissimulés.

Le code est extrêmement commenté...il n'utilise aucune API, aucune fonction avancée..le calcul est entièrement géré par le module appelé "Théorie". (211 lignes de code // 84 lignes de commentaires)
Le but était d'obtenir un code très souple et portable. On aurait certes pu utiliser les expressions régulières, ou un composant tout fait, mais dans ce cas là on perdait toute la portabilité de l'ensemble. Le fait d'avoir tout programmé from scratches permettrait ainsi d'exporter le code en n'importe quel langage en moins d'une demie heure, voire même de l'implanter sur un microprocesseur et de lancer sa propre calculette !

Le module "Graphique", quant à lui, "colorie" le code : il met en exposant les puissances, met le contenu des parenthèses en couleur...(utilisation du contrôle RichTextBox 6.0)(41 lignes de code // 8 lignes de commentaires)

Et la Form ne contient que 30 lignes...

La calculette gère les opérations standards : + - * / % (modulo), et ! (factorielle),ainsi que e (exponentielle), ln (logarithme népérien),tan et atn, avec gestion des priorités! C'est à dire que :
1 + 2/2 = 2
...ce qui pour l'ordinateur était loin d'être clair !

Il y a aussi un outil somme, qui s'utilise de la façon suivante :
somme([Nom_Variable]=[départ],[Arrivee],[Calcul])

Cette somme est incorporable dans n'importe quel calcul, et vous pouvez même effectuer des sommes à l'intérieur de sommes à l'intérieur de sommes à l'intérieur de sommes...bref!
Par exemple, le code suivant renvoie une approximation de PI en utilisant la méthode du développement limité d'arctangente :
4*somme(k=0,5000,(-1)^k*(1/(2*k+1))).

Plus d'informations

Si vous ne comprenez pas tout, lancez un calcul, et regardez la fenêtre exécution : elle affiche en temps réel les calculs effectués.

Par exemple :

Initialisation du calcul (sqr(e(ln((1+3)*(1/(2^2))))))^2
-------------------------------------------------------
     Math_It va effectuer le calcul (sqr(e(ln((1+3)*(1/(2^2))))))^2
          Math_It va effectuer le calcul sqr(e(ln((1+3)*(1/(2^2)))))
               Math_It va effectuer le calcul (e(ln((1+3)*(1/(2^2)))))
                    Math_It va effectuer le calcul (ln((1+3)*(1/(2^2))))
                         Math_It va effectuer le calcul ((1+3)*(1/(2^2)))
                              Math_It va effectuer le calcul 1+3
                                   Math_It va effectuer le calcul 3
3 a renvoyé 3
1+3 a renvoyé 4
                                        Math_It va effectuer le calcul 1/(2^2)
                                             Math_It va effectuer le calcul 2^2
                                                  Math_It va effectuer le calcul 2
2 a renvoyé 2
2^2 a renvoyé 4
1/(2^2) a renvoyé 0,25
(1+3)*(1/(2^2)) a renvoyé 1
ln((1+3)*(1/(2^2))) a renvoyé 0
e(ln((1+3)*(1/(2^2)))) a renvoyé 1
sqr(e(ln((1+3)*(1/(2^2))))) a renvoyé 1
                                                       Math_It va effectuer le calcul 2
2 a renvoyé 2
(sqr(e(ln((1+3)*(1/(2^2))))))^2 a renvoyé 1

Comme vous le voyez, le calcul se décompose sous plusieurs formes très simples...(ce qui est, rappelons le, le but de la récursivité !)

De plus, le code est extrêmement souple : si quelqu'un voulait par exemple créer un grapheur, il n'y aurait pas de grosses difficultés.
De même, si quelqu'un souhaitait le passer dans un autre langage, il lui suffirait de traduire le module théorie dans le bon langage...Comme ce module utilise des fonctions communes à tous les langages, il n'y a aucun problème.
Quant à la rapidité, c'est acceptable : pour un DL de 5000 termes, le temps d'exécution est très inférieur à 2 secondes.

A propos du débordement

Les calculs se font avec des variables Double...ce qui laisse assez de marges pour les calculs.

Téléchargement

Ce fichier n'est disponible que pour Windows, car il a été écrit en Visual Basic.

Fichier ZIP (EXE + Sources)

Fichier EXE

Code source

Feuille

Code source : Calculette.frm
  • Langage : vb
  • ΔT : 0.007s
  • Taille :2569 caractères
'2008
'neamar@neamar.fr

'A voir :
'Problème de commutativité de la soustraction : sqr(2) - 1 - ,4142 = PB....
'quand on marque 2e2, rajouter un * : 2*e2
'EN COURS Gestion plus poussée des erreurs
'CORRIGE ! Priorité de * et / par rapport à + et -
' CORRIGE ! 2^2^3 =>mauvais ordre
'EN COURS Ajout des fonctions
'Priorité du Modulo ?
'FINI Autoriser les très grands nombres && les nombres à virgules
'Autoriser les nombres décimaux en input
'Rafraichir les parenthèses
'Si on presse ², remplacer automatiquement par ^2
"^2"

'Ne pas autoriser les sauts de ligne, les considérer comme une validation du calcul
'Annule l'appui sur la touche
'Quand on décoche le bouton, remettre le texte à 0
'Si on le coche, on met le texte à 0..ce qui lance automatiquement la colorisation
'Calcul par défaut :
    Calcul.Text = "(sqr(e(ln((1+3)*(1/(2^2))))))^2""Fonctions supportées :""e | ln | sqr | + | - | * | / | % | ! |"'Ne pas afficher de messages
'Enlever les sauts de ligne
""), ")(", ")*(""Initialisation du calcul ""-------------------------------------------------------"

'Effectuer le calcul
'Et l'afficher
"       Calculs effectués en " & Pile_Appel & " sous parties."'Puis colorier le résultat

Modules

Module Théorie

Code source : Theorie.bas
  • Langage : vb
  • ΔT : 0.030s
  • Taille :17137 caractères
span class="co1">'Prend en paramètre une opération et deux nombres, et calcule...logique, non ?
"+""-""*""/""%" 'Modulo : reste de la division euclidienne : 5 % 2 = 1
"^" 'Puissance 4^2 = 4² = 16
'Renvoie la factorielle de n de facon récursive
    'Tire parti du fait que n! = n * (n+1)!

    'La factorielle n'est définie que sur N
"Impossible de calculer une factorielle négative !"'Pour calculer la factorielle, on utilise la définition originale : seulement sur des entiers ! On va pas non plus se taper des intégrales de e !
'Par conventiuon, 0! = 1
'Encore un peu de récursivité
'Cette fonction transforme un string passé en paramètre en nombre réel, c'est bien entendu la plus importante du programme !
    'Variables pour le calcul théorique :
'Somme => Contient la somme actuelle. C'est cette variable qui sera renvoyée à la fin.
    'Par définition, les opérations standards ont besoin de deux nombres : le nombre à gauche, et celui à droite.
    'Dernier_Nombre correspond au nombre de gauche
    'Nombre_Actuel correspond au nombre de droite, il change en quasi-permanence
    'Caractere_Actuel correspond au caractère en cours de traitement. (voir boucle for plus bas)
    'Chiffre_Actuel correspond au chiffre en cours de traitement, soit VAL(Caractere_Actuel) SI caractere_Actuel est un nombre.
    'AjouterApresVirgule : si le nombre en cours de traitement (Nombre_Actuel) n'est pas un entier, on met cette booléenne à true.
    'NbDecimales : le nombre de décimales du nombre...


    'Variables utilisées en pratique pour réussir la théorie ^^
'Augmenter de 1 la taille de la pile pour pouvoir afficher le nombre de  sous calculs effectués
    Pile_Appel = Pile_Appel + 1
    'Afficher le calcul dans le volet execution
"Math_It va effectuer le calcul "'Vérifier que le nombre de parenthèses ouvrantes = nombres de parenthèses fermantes
'Si probleme, on sort de la fonction...et on renvoie 0
'Si le calcul est de la forme "(.......)", on peut supprimer les parenthèses qui entourent
"("'On répete tant qu'on en a la possibilité

    'Initialiser les variables : on va faire semblant que le calcul commence par 0+[Expression].
    'On met donc Somme à 0, et Derniere_Operation à +
"+"'On enregistre la taille du string pour accélérer le traitement
'On va effectuer une boucle : pour chaque caractère, on réflechira comment le traiter
'Si le nombre est un nombre, l'ajouter au nombre en cours de traitement
'Si le caractere n'est pas un nombre, on va devoir faire un calcul...
            'Première chose à faire : mettre le nombre sous forme décimale si besoin est
'Si le caractère actuel n'est pas un nombre, ca complique le problème !
"("'Si on utilise une fonction directe (2(2+1), sous entendu 2*(2+1), il faut faire aparaitre le *
"*"'Effectuer la dernière opération si on est en présence d'une opération standard
"+""-""*""/""%""^"'On effectue la dernière opération que l'on devait faire
'L'opération est effectuée, vider la variable NombreActuel, stocker la prochiane opération
                Nombre_Actuel = 0
                'Et on stocke l'opération pour la prochaine fois
'A ce moment précis, dernièreopération=prochaine operation....
"^"'Si on a puissance, l'opération a priorité sur tout le reste ! On calcule l'exposant, et on applique direct.
"+""-"'On doit respecter la priorité des opéarations *,/ et ^par rapport à + et -
                    'Donc on subdivise le travail...+ étant commutatif, ce n'est pas un problème.
'Exit For
'Le caractère pour indiquer un nombre décimal est au choix . ou ,
",""."'Si ce n'est pas + - * /, c'est plus complexe :-)

                '///////////////////////////////////////////////////////////////////////////////////////
                'Fonctions supportées
                '///////////////////////////////////////////////////////////////////////////////////////
"e"'Exponentielle
                    'Cas de l'exponentielle
'On multiplie par NombreActuel de facon à corriger le cas 2e2 = 2*e2
"ln"'Logarithme népérien
"sqr"'Racine carrée
                    'Pour rappel, SQR(x) = x^(1/2)
"somme"'Faire une somme
"atn"'Racine carrée
                    'Pour rappel, SQR(x) = x^(1/2)
"!"'Factorielle


                '///////////////////////////////////////////////////////////////////////////////////////
                'Cas des parenthèses
                '///////////////////////////////////////////////////////////////////////////////////////
"("'Voilà le cas interessant du problème : si on a  des parenthèses...on fait appel à la récursivité.
                    'D'abord, il faut trouver la parenthèse correspondante, et l'envoyer à math_It (oui oui, la fonction dans laquelle on est !)
                    'ainsi, pour le calcul suivant : 2*(1+3) on va d'abord calculer (1+3) et ensuite * par 2
'Ca y est c'est terminé !
    'Ou presque...car il ne faut pas oublier qu'il reste le dernier calcul à effectuer (on effectue tous les calculs en retard...)
    'D'abord, calculer le prochain nombre.
'Ca y est, c'est terminé ! On effectue la dernière opération avec les derniers nombres, et on renvoie.
" a renvoyé "'On arrête l'affichage du debugger temporairement pour ne pas surcharger la fenetre
'D'abord, trouver les paramètres

    'Le nom de la variable
"="'Sa valeur de départ
","'Et sa valeur d'arrivée
","'Et enfin, le calcul qui devra être effectué !
"La somme " & Calcul; " va être effectué de " & Start & " à " & Fin & " (variable : "")"

    'Lancer le calcul
'D'abord, remplacer la variable par sa valeur
'Debug.Print Calcul_Temporaire, Math_It(Calcul_Temporaire)
'On arrête l'affichage du debugger temporairement pour ne pas surcharger la fenetre
"La somme n'est pas correcte !"'Cette fonction recoit en paramètre un nombre et un string, elle renvoie le nombre avec le string ajouté.
    'Par exemple, elle prend en paramètre 125,"7" et renvoie 1257
    'NbChiffres = Int(Log(Nombre) / 2.30258509299405) 'Calcule le Log en base 10 du nombre, C.a.d son nombre de décimales
'i est l'endroit ou commencer la recherche
'TrouverFinBLoc prend en paramètre une expression et renvoie la fin du bloc commmencant à l'emplacement i
'| => valeur de i
'Par  exemple, pour 1+ | (2+5) *2, le bloc se termine après )
'Ou encore, pour 2^|25+2, le bloc prend en compte 25 et rien d'autre
"("'Cas x^(2*2)
'Ce calcul n'est pas complètement optimisé
    'Par exemple, pour (3+5), le programme effectuera d'abord 3+5 PUIS (3+5), d'ou encombrement inutile de la pile d'appel
'C'est un simple nombre..on trouve les chiffres, et on renvoie les nombres
"^""-"'C'est tout...
'Trouver la parenthèse fermante associée à une certaine parenthèse
'InStr(i + 1, Chaine, ")") - i - 1)) ne fonctionne pas si l'on a imbrication de parenthèes, e.g (1+5(1+3)+2)
"("'A chaque fois qu'on rencontre une (, on dit qu'il faudra une ) de plus avant de sortir
")"'Quand on sort, c'est qu'on a la parenthèse...ou qu'elle n'existe pas !
'Cette fonction au nom pompeux vérifie que le nombre de parenthèses "(" est égale au nombre de ")". Logique, non ?
'Si il y a un pb, elle remplit la variable globale Erreur et renvoie False
"(""("")"")""ERREUR : " & CompteurOuvrantes & " '(' trouvés, et " & CompteurFermantes & " ')'. [sous partie : ""]"

Module Graphique

Code source : Graphique.bas
  • Langage : vb
  • ΔT : 0.003s
  • Taille :2113 caractères
span class="co1">'Colorie le texte à l'intérieur d'un RichTextBox fourni en paramètre.
'Créer un tableau avec les couleurs à utiliser
'Sauvegarder la position intiale du curseur
'Remettre la mise en forme à 0
'Boucler sur tous les caractères
'Si parenthèses : trouver la parenthèse fermante associée et colorier
"("'Trouver la prochaine parenthèse et colorier le tout
'Si on a des puissances, on met en hauteur pour avoir une jolie visualisation graphique du concept
"^"'Et en plus, on diminue la taille
Auteur
Neamar
Date
Mai 2008
But
La récursivité
Langage
Visual Basic
Menu
Index des ressources

Chargement du sommaire...