Opera : des opérations arithmétiques en nombres rationnels

Soyez le premier à donner votre avis sur cette source.

Vue 235 fois - Téléchargée 13 fois

Description

Opera est un langage de programmation qui effectue des opérations arithmétiques mathématiquement exactes en utilisant des nombres rationnels. Le logiciel Opera complet est disponible à : https://github.com/pgl10/Opera en versions Windows et Linux. Il est public et gratuit. Il est fourni avec les sources en C++, les exécutables, les exemples et la documentation en français. Le projet ici présent en contient tous les fichiers à l'exception des exécutables.

Opera permet d'effectuer les opérations arithmétiques prévues sur des nombres rationnels représentés par des fractions de deux entiers aussi grands que nécessaires. Il a deux modes de fonctionnement : le mode conversationnel et l'exécution de fichiers de commandes. Opera.pdf est le manuel de l'utilisateur et description.pdf en est une simple présentation rapide.

Le code source comporte des commentaires. Ci-après le code C++ pour le calcul d'une expression arithmétique avec ou sans parenthèses et comportant l'emploi des opérateurs utilisables avec leurs priorités relatives.

Source :

// Pour calculer le contenu des parenthèses
bool parentheses(std::string& ligne) {
    std::string ops = "^/*-+<>";
    bool pars;
    do {
        pars = false;
        std::size_t found = ligne.find('(');
        // S'il y a encore des parenthèses dans ligne
        if(found != std::string::npos) {
            pars = true;
            std::string front, back;
            if(found == 0) front = "";
            else {
                front = ligne.substr(0, found);
                if(ops.find(front[front.size()-1]) == std::string::npos) return false;
            }
            std::size_t last = std::string::npos;
            int niv = 0;
            unsigned int suiv = found + 1;
            while(suiv < ligne.size()) {
                if(ligne[suiv] == ')' && niv == 0) {
                    last = suiv;
                    break;
                }
                if(ligne[suiv] == '(') niv = niv + 1;
                if(ligne[suiv] == ')') niv = niv - 1;
                suiv = suiv + 1;
            }
            if(last == std::string::npos) return false;
            // ligne[found] = '(' correspond à ligne[last] = ')'
            if(last == ligne.size()-1) back = "";
            else {
                back = ligne.substr(last+1);
                if(ops.find(back[0]) == std::string::npos) return false;
            }
            std::string subligne = ligne.substr(found+1, last-found-1);
            // S'il faut effectuer le même traitement à subligne 
            if(subligne.find('(') != std::string::npos) parentheses(subligne);
            bigRa subval;
            if(eval(subligne, subval)) {
                static int num = 0;
                num = num+1;
                std::stringstream stream;
                stream << num;
                std::string name;
                // on utilise une nouvelle variable auxiliaire
                name = "&t" + stream.str();
                archiverra(lect, name, subval);
                ligne = front + name + back;
            }
            else return false;
        }    
    }while(pars);
    return true;
}

// Pour calculer la valeur d'une instruction définie
bool eval(std::string ligne, bigRa& r) {
    if(ligne.size() == 0) return false;
    std::string ops = "^/*-+<>";
    std::size_t found;
    found = ops.find(ligne[ligne.size()-1]);
    // le dernier caractère de ligne ne doit pas être un opérateur
    if(found != std::string::npos) return false;
    if(!parentheses(ligne)) return false;
    if(cval(ligne, r)) return true;
    // ligne[pos] est-il un opérateur ayant 2 opérandes ?
    std::size_t pos=std::string::npos;
    found = ligne.find_last_of("^");
    if(found != std::string::npos) pos=found;
    found = ligne.find_last_of("/");
    if(found != std::string::npos) pos=found;
    found = ligne.find_last_of("*");
    if(found != std::string::npos) pos=found;
    found = ligne.find_last_of("-");
    if(found != std::string::npos) {
        std::size_t pm=found;
        bool bon = true;
        if(pm > 0 && ligne[pm-1] == '^') bon = false;
        if(pm > 0 && ligne[pm-1] == '*') bon = false;
        if(pm > 0 && ligne[pm-1] == '/') bon = false;
        if(pm > 0 && ligne[pm-1] == '+') bon = false;
        while(pm > 0 && ligne[pm-1] == '-') pm = pm-1;
        if(pm > 0 && ligne[pm-1] == '<') bon = false;
        if(pm > 0 && ligne[pm-1] == '>') bon = false;
        if(pm == 0) {
            ligne = "0" + ligne;
            pm = pm+1;
        }
        // bon = true  pour l'opérateur - à deux opérandes
        // bon = false pour l'opérateur - à un seul argument
        if(bon) pos = pm;
    }
    found = ligne.find_last_of("+");
    if(found != std::string::npos) pos=found;
    found = ligne.find_last_of("<");
    if(found != std::string::npos) pos=found;
    found = ligne.find_last_of(">");
    if(found != std::string::npos) pos=found;
    // s'il n'y a ici aucun opérateur : instruction non reconnue
    if(pos == std::string::npos) return false;
    // si un opérateur binaire est au début ou à la fin de ligne
    // l'instruction ne sera pas reconnue
    if(pos==(ligne.size()-1) || pos==0) return false;
    std::string left = ligne.substr(0, pos);
    std::string right = ligne.substr(pos+1);
    // r1 et r2 seront les deux opérandes
    bigRa r1, r2;
    if(!cval(left, r1)) 
        if(!eval(left, r1)) return false;
    if(!cval(right, r2)) 
        if(!eval(right, r2)) return false;
    // calcul final du résultat
    if(ligne[pos] == '^') {
        if(r2.getDen() > INT_MAX) {
            std::cout << "Exponentiation hors limites" << std::endl;
            return false;
        }
        int n2d = r2.getDen();
        if(n2d == 0) return false;
        if(n2d > 1) {
            int sr1 = 1;
            if(cmpRa(r1, 0) < 0) {
                if((n2d&1) == 0) {
                    aout("Ce calcul est impossible.n");
                    return false;
                }
                sr1 = -1; 
            }
            bigRa t1 = r1;
            bool good = true;
            Integer rr1n = racine(n2d, sr1*r1.getNum());
            Integer r1n = sr1;
            for(int i=0; i<n2d; i++) r1n = r1n*rr1n;
            if(r1n == r1.getNum()) r1.setNum(sr1*rr1n);
            else {
                irrationnel();
                good = false;
            }
            Integer rr1d = racine(n2d, r1.getDen());
            Integer r1d = 1;
            for(int i=0; i<n2d; i++) r1d = r1d*rr1d;
            if(r1d == r1.getDen()) r1.setDen(rr1d);
            else {
                if(good) irrationnel();
                good = false;
            }
            if(!good) {
                r1 = t1;
                if(sr1 > 0) r1 = nroot(r1, n2d, 64);
                else r1 = -nroot(-r1, n2d, 64);
            }
        }
        if(r2.getNum() > INT_MAX) {
            std::cout << "Exponentiation hors limites" << std::endl;
            return false;
        }
        int n2n = r2.getNum();
        r=r1.puissance(n2n);
    }
    if(ligne[pos] == '/') r=r1.diviser(r2);
    if(ligne[pos] == '*') r=r1.multiplier(r2);
    if(ligne[pos] == '-') r=r1.soustraire(r2);
    if(ligne[pos] == '+') r=r1.additionner(r2);
    if(ligne[pos] == '<') r=cmpRa(r2, r1);
    if(ligne[pos] == '>') r=cmpRa(r1, r2);
    r.simplifier();
    return true;
}

Exemple :

Voici un exemple de code source en langage Opera qui calcule une suite d'approximations successives de pi jusqu'à celle dont la valeur comporte c chiffres décimaux exacts.

# Calcul d'une approximation de pi ayant c chiffres décimaux exacts
# La dernière fraction obtenue a certainement cette propriété.
# pi = 4 / (1 + 1² / (3 + 2² / (5 + 3² / (7 + 4² / (9 + ... )))))
c = 36
f[1] = 3
f[2] = 19/6
delta = f[2]-f[1]
n=2
delta > f[n]/10^c
boucle
  n = n + 1
  k = n
  d =  (2*k-1)+k^2/(2*k+1)
  k > 1
  boucle
    k = k-1
    d = (2*k-1)+k^2/d
  retour
  f[n] = 4/d
  delta = f[n]-f[n-1]
  si delta < 0
    delta = -delta
retour
garder resultat.txt

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.