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

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 nombreux exemples et la documentation en français. Le projet ici présent en contient tous les fichiers à l'exception des exécutables.

Opera effectue 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 utilise 7 opérateurs ayant deux opérandes, l'opérateur - ayant un seul opérande et de nombreuses fonctions spécifiques. 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 en C++ affiché ci-après montre le calcul d'une expression arithmétique avec ou sans parenthèses en respectant les priorités relatives des opérateurs utilisés. Et l'exemple affiché ensuite montre le calcul d'une approximation du nombre pi.

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) {
        if(found == 0) {
            ligne = "0" + ligne;
            found = 1;
        }
        pos = found;
        std::size_t opb1 = ops.find(ligne[found-1]);
            if(opb1 != std::string::npos) {
            unsigned int k;
            bool op = false;
                for(k=found+1; k < ligne.size(); k++) {
                std::size_t nop = ops.find(ligne[k]);
                if(nop != std::string::npos) {op=true; break;}
            }
            if(op) ligne = ligne.substr(0, found) + "(" + ligne.substr(found, k-found) + ")" + ligne.substr(k);
            else ligne = ligne.substr(0, found) + "(" + ligne.substr(found, k-found) + ")";	
            if(!eval(ligne, r)) return false;
            r.simplifier();
            return true;
        }
    }
    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;
                int k = approxim(0);
                if(sr1 > 0) r1 = nroot(r1, n2d, k);
                else r1 = -nroot(-r1, n2d, k);
            }
        }
        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.

# Ce programme pi.txt en langage Opera calcule une approximation de pi 
# ayant c chiffres décimaux exacts en utilisant la formule suivante.
# 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
pi = f[n]
enti pi*10^c
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.