Exemple de programme en C++
Calculs sur un nombre de bits arbitraire
Après avoir lu un article qui présentait un problème plutôt saugrenu, il m'est venu l'idée de définir une classe C++ qui permettrait de faire des calculs manipulant des valeurs entières codées sur un nombre de bits choisi par le programmeur. Cette classe devait comporter au minimum les 4 opérations de base (+, -, *, /), la négation (- unaire) et les comparaisons (==, !=, >, <=, <, >=). Parce que c'était facile et pratique, j'y ai ajouté des opérateurs classiques du C et du C++ comme +=, -=, *=, /=, %, %=, ++, --. En fait, il s'agit d'un template (une classe générique) paramétré par le nombre de tranches de 16 bits qui stockeront les données traitées. La classe générique s'appelle Verylong et il est par exemple possible de déclarer des variables de 128 bits de la façon suivante :
Verylong<128/16> a, b;
Les choses se passent comme si les calculs étaient réalisés en base 65536 (2 à la puissance 16) avec les retenues correspondantes. Les nombres peuvent être signés (cas par défaut, le signe étant alors décrit par le bit de poids le plus élevé) ou non. Comme cela est habituel en C++, la déclaration de la classe est placée dans un fichier à inclure ayant le même nom qu'elle (verylong.h) ou au moins qui s'en rapproche. Comme il s'agit d'un template, qui sera développé chaque fois qu'un nouveau format de Verylong sera employé, la définition aussi est dans le fichier à inclure. Au niveau du programme principal ou module qui l'utilise, on pourra déclarer des variables de plusieurs tailles différentes (il y a peu de raison d'en créer qui aient 32 bits ou moins, sauf pour le traitement des overflows) sous réserve que le nombre de warnings générés ne dépasse pas une limite fixée par le compilateur, après la ligne suivante :
#include <verylong.h>
Le fichier verylong.h n'a bien sûr pas lieu d'être modifié à moins d'y découvrir un ou plusieurs bugs. En voici le code source :
#ifndef VERYLONG_H #define VERYLONG_H #include <ctype.h> #include <string.h> typedef unsigned short ushort; typedef unsigned long ulong; #define MASQ_SIGN (ushort)0x8000U #define SZ_CELL (sizeof(ushort)*8) template <unsigned N> class Verylong { ushort Valeur[N]; bool Signe; static char Buffer[]; static char* IniNonSigne; static int Bit_Non_Zero(ushort Val); static int Cellule_Non_Zero(ushort Tab[], int Taille); int Cellule_Non_Zero() { return Cellule_Non_Zero(Valeur,N); }; void SetBit(int Ind) throw(Message); Verylong& Neg() throw(Message); Verylong& Decalage(int Nbr, int Lim = 0) throw(Message); public: Verylong(long Ini = 0, bool Sig = true, bool Strict = true) throw(Message); Verylong(const char* Ini, bool Sig = true) throw(Message); Verylong(const ushort Ini[], int Nbr = N, bool Sig = true) throw(Message); Verylong& operator=(const Verylong<N>& Val); Verylong operator+(const Verylong<N>& Val) const; Verylong& operator+=(const Verylong<N>& Val) throw(Message); Verylong& operator++() throw(Message); Verylong operator-(const Verylong<N>& Val) const; Verylong& operator-=(const Verylong<N>& Val) throw(Message); Verylong& operator--() throw(Message); Verylong operator-() const; Verylong operator*(const Verylong<N>& Val) const; Verylong& operator*=(const Verylong<N>& Val) throw(Message); Verylong operator/(const Verylong<N>& Val) const; Verylong& operator/=(const Verylong<N>& Val); Verylong operator%(const Verylong<N>& Val) const; Verylong& operator%=(const Verylong<N>& Val); void Division(const Verylong<N>& Diviseur, Verylong<N>& Quotient, Verylong<N>& Reste) const throw(Message); Verylong abs() const; void Affichage(int Largeur = 0, bool ADroite = false); char* Formater(char* Buff = NULL); operator long() const throw(Message); bool EstNegatif() const { return (Signe && (Valeur[0] >= MASQ_SIGN)); }; bool operator==(const Verylong<N>& Val) const; bool operator!=(const Verylong<N>& Val) const { return !(*this == Val); }; bool operator>(const Verylong<N>& Val) const; bool operator<=(const Verylong<N>& Val) const { return !(*this > Val); }; bool operator<(const Verylong<N>& Val) const { return !((*this == Val) || (*this > Val)); }; bool operator>=(const Verylong<N>& Val) const { return ((*this == Val) || (*this > Val)); }; }; template <unsigned N> char Verylong<N>::Buffer[(((N * SZ_CELL) / 10) + 1) * 4]; template <unsigned N> int Verylong<N>::Bit_Non_Zero(ushort Val) { ushort Masque = 0x8000U; for (int i = 0; i < SZ_CELL; i++, Masque >>= 1) { if ((Val & Masque) != 0) return i; } return SZ_CELL; } template <unsigned N> int Verylong<N>::Cellule_Non_Zero(ushort Tab[], int Taille) { int i = 0; while ((i < Taille) && (Tab[i] == 0)) i++; return i; } template <unsigned N> void Verylong<N>::SetBit(int Ind) throw(Message) { if ((Ind >= 0) && (Ind < (N * SZ_CELL))) Valeur[N-(Ind / SZ_CELL)-1] |= (ushort)(1 << (Ind % SZ_CELL)); else throw(Message("SetBit en dehors des limites d'un Verylong")); } template <unsigned N> Verylong& Verylong<N>::Neg() throw(Message) { for (int i = 0; i < N; i++) Valeur[i] = (ushort)~Valeur[i]; try { ++(*this); } catch(Message Me) { throw(Message("Overflow sur Negation de Verylong")); }; return *this; } // Cette fonction de décalage des bits est limitée à 15 bits vers la // droite ou la gauche. Comme elle ne traite pas tous les cas je la place // dans la partie privée plutôt que de la complexifier template <unsigned N> Verylong& Verylong<N>::Decalage(int Nbr, int Lim) throw(Message) { if ((Lim < 0) || (Lim >= N)) throw(Message("Decalage d'un verylong en dehors des limites")); else if ((Nbr > 0) && (Nbr < SZ_CELL)) { ulong Donnee = (ulong)Valeur[Lim] << (SZ_CELL + Nbr); for (int i = Lim+1; i < N; i++, Donnee <<= SZ_CELL) { Donnee |= (ulong)Valeur[i] << Nbr; Valeur[i-1] = (ushort)(Donnee >> SZ_CELL); } Valeur[N-1] = (ushort)(Donnee >> SZ_CELL); } else if ((Nbr < 0) && (Nbr > -SZ_CELL)) { int Decal = -Nbr; ulong Donnee = Valeur[N-1] >> Decal; for (int i = N-2; i >= Lim; i--, Donnee >>= SZ_CELL) { Donnee |= (ulong)Valeur[i] << (SZ_CELL - Decal); Valeur[i+1] = (ushort)Donnee; } Valeur[Lim] = (ushort)Donnee; } else if (Nbr != 0) { char Excep[100]; sprintf(Excep,"Decalage d'un Verylong de plus de %d bits",SZ_CELL); throw(Message(Excep)); } return *this; } template <unsigned N> char* Verylong<N>::IniNonSigne = "Initialisation d'un Verylong non signe avec une valeur negative"; // Constructeurs template <unsigned N> Verylong<N>::Verylong(long Ini, bool Sig, bool Strict) throw(Message) : Signe(Sig) { if (sizeof Valeur < sizeof(long)) throw(Message("Initialisation d'un Verylong de moins de 32 bits par un entier long")); else if ((Ini >= 0) || (!Strict && !Signe)) { for (int i = 0; i < (N-2); i++) Valeur[i] = 0; } else { if (!Signe) throw(Message(IniNonSigne)); for (int i = 0; i < (N-2); i++) Valeur[i] = (ushort)-1; } Valeur[N-2] = (ushort)((ulong)Ini >> SZ_CELL); Valeur[N-1] = (ushort)Ini; } template <unsigned N> Verylong<N>::Verylong(const char* Ini, bool Sig = true) throw(Message) : Signe(Sig) { Verylong<N> Dix(10L); const char* Over = "Overflow en initialisation de Verylong"; bool Negatif = false; char* Trav = (char*)Ini; memset(Valeur,0,sizeof Valeur); while ((*Trav != '\0') && isspace(*Trav)) Trav++; switch(*Trav) { case '\0': return; case '-': if (Signe) { Negatif = true; Trav++; } else throw(Message(IniNonSigne)); break; } while (*Trav != '\0') { char Car = *Trav++; if (isdigit(Car)) { try { *this = (*this * Dix) + Verylong<N>(Car - '0'); } catch(Message Me) { throw(Message(Over)); }; } else throw(Message("Caractere anormal dans une initialisation de Verylong")); } if (Negatif) { try { Neg(); } catch(Message Me) { throw(Message(Over)); }; } } template <unsigned N> Verylong<N>::Verylong(const ushort Ini[], int Nbr, bool Sig) throw(Message) : Signe(Sig) { int i,j; if (Nbr > N) throw(Message("Tableau trop long dans une initialisation de Verylong")); for (i = N-1, j = Nbr-1; (i >= 0) && (j >= 0); i--, j--) Valeur[i] = Ini[j]; if (i >= 0) { ushort Rempli = (ushort)(Signe && ((Ini[0] & MASQ_SIGN) != 0) ? ~0 : 0); for (; i >= 0; i--) Valeur[i] = Rempli; } } // Contrairement au constructeur par recopie (implicite), l'opérateur // d'affectation duplique la valeur mais pas ce qui indique que le nombre // est signé ou pas (pour autoriser les conversions) template <unsigned N> Verylong& Verylong<N>::operator=(const Verylong<N>& Val) { for (int i = 0; i < N; i++) Valeur[i] = Val.Valeur[i]; return *this; } // Si les 2 nombres de part et d'autre de + ou += sont l'un signé, l'autre // pas, le nombre de droite est "converti" pour l'opération dans le type // de celui de gauche template <unsigned N> Verylong Verylong<N>::operator+(const Verylong<N>& Val) const { Verylong<N> Tempo = *this; Tempo += Val; return Tempo; } template <unsigned N> Verylong& Verylong<N>::operator+=(const Verylong<N>& Val) throw(Message) { ulong Trav, Retenue = 0L; ulong Prevu = Signe ? 1 : 0; ushort SigPrec = Valeur[0] & MASQ_SIGN; bool OvPossib = Signe && (SigPrec == (Val.Valeur[0] & MASQ_SIGN)); for (int i = N-1; i >= 0; i--) { Trav = (long)Valeur[i] + (long)Val.Valeur[i] + Retenue; Valeur[i] = (ushort)Trav; Retenue = Trav >> SZ_CELL; } if ((Retenue > Prevu) || (OvPossib && ((Valeur[0] & MASQ_SIGN) != SigPrec))) throw(Message("Overflow sur + ou += de Verylong")); return *this; } template <unsigned N> Verylong& Verylong<N>::operator++() throw(Message) { int i = N; do Valeur[--i]++; while ((Valeur[i] == 0) && (i > 0)); if ((i == 0) && (Valeur[0] == (Signe ? MASQ_SIGN : 0))) throw(Message("Overflow sur ++ de Verylong")); return *this; } // Si les 2 nombres de part et d'autre de - ou -= sont l'un signé, l'autre // pas, le nombre de droite est "converti" pour l'opération dans le type // de celui de gauche template <unsigned N> Verylong Verylong<N>::operator-(const Verylong<N>& Val) const { Verylong<N> Tempo = *this; Tempo -= Val; return Tempo; } template <unsigned N> Verylong& Verylong<N>::operator-=(const Verylong<N>& Val) throw(Message) { ulong Trav, Retenue = 0L; ulong Prevu = Signe ? 1 : 0; ushort SigPrec = Valeur[0] & MASQ_SIGN; bool OvPossib = Signe && (SigPrec != (Val.Valeur[0] & MASQ_SIGN)); for (int i = N-1; i >= 0; i--) { Trav = (long)Valeur[i] - (long)Val.Valeur[i] - Retenue; Valeur[i] = (ushort)Trav; Retenue = -(short)(Trav >> SZ_CELL); } if ((Retenue > Prevu) || (OvPossib && ((Valeur[0] & MASQ_SIGN) != SigPrec))) throw(Message("Overflow sur - ou -= de Verylong")); return *this; } template <unsigned N> Verylong& Verylong<N>::operator--() throw(Message) { int i = N; do Valeur[--i]--; while ((Valeur[i] == (ushort)~0) && (i > 0)); if ((i == 0) && (Valeur[0] == (Signe ? (ushort)~MASQ_SIGN : (ushort)~0))) throw(Message("Overflow sur -- de Verylong")); return *this; } // Pour une négation, la valeur est toujours considérée comme signée template <unsigned N> Verylong Verylong<N>::operator-() const { Verylong<N> Tempo; Tempo = *this; return Tempo.Neg(); } // Si les 2 nombres de part et d'autre de * ou *= sont l'un signé, l'autre // pas, le nombre de droite est "converti" pour l'opération dans le type // de celui de gauche template <unsigned N> Verylong Verylong<N>::operator*(const Verylong<N>& Val) const { Verylong<N> Tempo = *this; Tempo *= Val; return Tempo; } template <unsigned N> Verylong& Verylong<N>::operator*=(const Verylong<N>& Val) throw(Message) { #define TAILLE_RES N*2 ushort Resul[TAILLE_RES]; bool Negatif; Verylong<N> Multi = Val; if (Signe) { ushort Sig1 = Valeur[0] & MASQ_SIGN, Sig2 = Multi.Valeur[0] & MASQ_SIGN; Negatif = ((Sig1 ^ Sig2) != 0); if (Sig1 != 0) Neg(); if (Sig2 != 0) Multi.Neg(); } else Negatif = false; int Lim1 = Multi.Cellule_Non_Zero(); int Lim2 = Cellule_Non_Zero(); for (int i = 0; i < TAILLE_RES; i++) Resul[i] = 0; for (int i = N-1; i >= Lim1; i--) { if (Multi.Valeur[i] != 0) { ulong Retenue = 0L; for (int j = N-1; j >= Lim2; j--) { ulong Trav = (ulong)Valeur[j] * (ulong)Multi.Valeur[i]; Trav += Retenue + Resul[i+j+1]; Resul[i+j+1] = (ushort)Trav; Retenue = Trav >> SZ_CELL; } Resul[i+Lim2] += (ushort)Retenue; } } for (int i = TAILLE_RES-1, j = N-1; j >= 0; i--,j--) Valeur[j] = Resul[i]; int Lim = Cellule_Non_Zero(Resul,TAILLE_RES); if (((TAILLE_RES - Lim) > N) || EstNegatif()) throw(Message("Overflow sur * ou *= de Verylong")); if (Negatif) Neg(); return *this; } // Si les 2 nombres de part et d'autre d'une des fonctions de division ou // reste sont l'un signé, l'autre pas, le nombre de droite est "converti" // pour l'opération dans le type de celui de gauche template <unsigned N> Verylong Verylong<N>::operator/(const Verylong<N>& Val) const { Verylong<N> Quotient,Reste; Division(Val,Quotient,Reste); return Quotient; } template <unsigned N> Verylong& Verylong<N>::operator/=(const Verylong<N>& Val) { Verylong<N> Quotient,Reste; Division(Val,Quotient,Reste); return (*this = Quotient); } template <unsigned N> Verylong Verylong<N>::operator%(const Verylong<N>& Val) const { Verylong<N> Quotient,Reste; Division(Val,Quotient,Reste); return Reste; } template <unsigned N> Verylong& Verylong<N>::operator%=(const Verylong<N>& Val) { Verylong<N> Quotient,Reste; Division(Val,Quotient,Reste); return (*this = Reste); } template <unsigned N> void Verylong<N>::Division(const Verylong<N>& Diviseur, Verylong<N>& Quotient, Verylong<N>& Reste) const throw(Message) { const Verylong<N> Zero_Vl(0L); if (Diviseur == Zero_Vl) throw(Message("Division par 0 sur un Verylong")); else if (*this == Zero_Vl) Quotient = Reste = Zero_Vl; else { bool Negatif, Reste_Neg; Verylong<N> Div_eur = Diviseur, Div_ende = *this; if (Signe) { ushort Sig1 = Valeur[0] & MASQ_SIGN, Sig2 = Div_eur.Valeur[0] & MASQ_SIGN; Negatif = ((Sig1 ^ Sig2) != 0); Reste_Neg = (Sig1 != 0); if (Sig1 != 0) Div_ende.Neg(); if (Sig2 != 0) Div_eur.Neg(); } else Reste_Neg = Negatif = false; if (Div_eur > Div_ende) { Quotient = Zero_Vl; Reste = Div_ende; } else { Verylong<N> Trav = Div_eur; int Lim1 = Div_ende.Cellule_Non_Zero(); int Lim2 = Trav.Cellule_Non_Zero(); if ((Lim1 >= N) || (Lim2 >= N)) throw(Message("Anomalie detectee dans le deroulement d'une Division")); int Lim_b1 = Bit_Non_Zero(Div_ende.Valeur[Lim1]); int Lim_b2 = Bit_Non_Zero(Trav.Valeur[Lim2]); if (Lim1 < Lim2) { int Decal = Lim2 - Lim1; for (int i = 0; i < N; i++) Trav.Valeur[i] = ((i + Decal) < N) ? Trav.Valeur[i+Decal] : (ushort)0; } Trav.Decalage(Lim_b2 - Lim_b1,Lim1); ulong Indice = ((Lim2 - Lim1) * SZ_CELL) + (Lim_b2 - Lim_b1); for (Quotient = Zero_Vl; Div_ende >= Div_eur; Indice--) { if (Div_ende >= Trav) { Quotient.SetBit(Indice); Div_ende -= Trav; } Trav.Decalage(-1,Lim1); if ((Trav.Valeur[Lim1] == 0) && (Lim1 < (N-1))) Lim1++; } Reste = Div_ende; } if (Negatif) Quotient.Neg(); if (Reste_Neg) Reste.Neg(); } } template <unsigned N> Verylong Verylong<N>::abs() const { if (EstNegatif()) { Verylong<N> Tempo = *this; return Tempo.Neg(); } else return *this; } template <unsigned N> void Verylong<N>::Affichage(int Largeur, bool ADroite) { char* Chaine = Formater(); int Nbr = strlen(Chaine); if ((Largeur > 0) && ADroite && (Nbr < Largeur)) { for (int i = Nbr; i < Largeur; i++) putchar(' '); } printf("%s",Chaine); if ((Largeur > 0) && !ADroite && (Nbr < Largeur)) { for (int i = Nbr; i < Largeur; i++) putchar(' '); } } // Une optimisation était nécessaire pour accélérer l'affichage des valeurs // ayant un grand nombre de décimales en réduisant l'utilisation de la // division de Verylong (facteur 6) template <unsigned N> char* Verylong<N>::Formater(char* Buff) { const Verylong<N> Zero_Vl(0L,Signe); Verylong<N> Trav = *this, Un_Million(1000000L,Signe); Verylong<N> Quotient = Zero_Vl, Reste = Zero_Vl; // Le tableau communiqué Buff doit avoir un taille suffisante int Ind = sizeof Buffer; bool Negatif = EstNegatif(), DernTranche; if (Buff == NULL) Buff = Buffer; Buff[--Ind] = '\0'; if (Negatif) Trav = -Trav; do { Trav.Division(Un_Million,Quotient,Reste); DernTranche = (Quotient == Zero_Vl); long Tranche = Reste; int Cmpt = 0; do { Buff[--Ind] = (char)((Tranche % 10) + '0'); Tranche /= 10; Cmpt++; } while ((Tranche > 0) || (!DernTranche && (Cmpt < 6))); Trav = Quotient; } while (!DernTranche); if (Negatif) Buff[--Ind] = '-'; return &Buff[Ind]; } template <unsigned N> Verylong<N>::operator long() const throw(Message) { long Trav = Valeur[N-1]; Trav += Valeur[N-2] << SZ_CELL; if (*this != Verylong<N>(Trav,Signe,false)) throw(Message("Conversion de Verylong en long rendant une valeur erronee")); return Trav; } template <unsigned N> bool Verylong<N>::operator==(const Verylong<N>& Val) const { bool Egaux = ((Signe == Val.Signe) || (((Valeur[0] | Val.Valeur[0]) & MASQ_SIGN) == 0)); for (int i = 0; Egaux && (i < N); i++) Egaux = (Valeur[i] == Val.Valeur[i]); return Egaux; } template <unsigned N> bool Verylong<N>::operator>(const Verylong<N>& Val) const { bool Egaux = ((Signe == Val.Signe) || (((Valeur[0] | Val.Valeur[0]) & MASQ_SIGN) == 0)); int i; for (i = 0; Egaux && (i < N); i++) Egaux = (Valeur[i] == Val.Valeur[i]); switch(i) { case 0: if ((Valeur[0] & MASQ_SIGN) != 0) return !Signe; else return !Val.Signe; case 1: { ushort PFort1 = Valeur[0] & MASQ_SIGN, PFort2 = Val.Valeur[0] & MASQ_SIGN; if ((PFort1 != 0) && (PFort2 == 0)) return !Signe; else if ((PFort1 == 0) && (PFort2 != 0)) return Signe; } } return (Valeur[i-1] > Val.Valeur[i-1]); } #endif
Je ne décrirai pas toutes les particularités mais soulignerai simplement des éléments propres au C++. Ceux qui en ont besoin devront consulter des livres de référence courants (ex : concernant les opérations bit à bit &, |, ^, ~).
Tout d'abord, les exceptions. Comme le nom l'indique, elles ne doivent pas se produire dans le déroulement normal du programme et signalent des anomalies, comme des débordements, qui pourraient passer inaperçues lors de calculs réalisés sur les types standards du C++ (int, long, ). Une exception est générée par l'instruction :
throw(Objet( ));
et elle est traitée par :
try { /* Code pouvant générer l'exception */ } catch(Objet o) { /* Traitement selon l'objet o */ }
Dans un objet, il existe des fonctions spéciales nommées constructeurs (qui ont le même nom que la classe) et destructeur (ex : ~Message()). Un des constructeurs est appelé lors de la déclaration de l'objet (instanciation) et le destructeur est appelé s'il y a lieu à la sortie de la zone ou l'objet est visible (comme la fin d'une fonction). En fait, en temps normal aucun destructeur n'est nécessaire tant que l'objet ne réalise pas d'allocation de mémoire (par exemple par new / delete).
L'un des constructeurs est particulier, le constructeur par recopie. Celui ci peut être implicite, comme pour Verylong (il s'agit alors d'une simple copie conforme de ce qui est à l'emplacement de l'objet dupliqué). Il sera utilisé lors de déclaration comme :
Verylong<4> a = b; /* b déclaré Verylong<4> auparavant */
mais aussi pour des duplications implicites pendant les traitements. J'ai dû créer un constructeur par recopie explicite pour la classe Message - dans le programme d'essai, pour le traitement des exceptions - en me rendant compte que des appels transparents du destructeur se produisaient et que la zone mémoire allouée pour Texte était donc perdue pour des copies nouvellement créées.
Que ce soit pour les constructeurs, les fonctions habituelles ou pour les opérateurs, il est possible de définir dans une classe d'objets plusieurs fonctions portant le même nom (c'est la surcharge). Ce sont le nombre ou les types de leurs différents paramètres qui les différencieront au moment de l'appel à une fonction particulière. Les opérateurs redéfinis surchargent les opérateurs standards du C++ sur les autres types ou classes. Il peut arriver que le compilateur soit incapable de choisir entre 2 ou plusieurs fonctions, mais cela provoque un message d'erreur.
En regardant le code du template et du programme d'essai plus loin, vous pouvez vous rendre compte que le C++ permet de déclarer des variables à peu près n'importe où dans un bloc de code et pas seulement à son début. Ceci suit la logique de ce langage car un objet est normalement toujours initialisé d'une façon ou d'une autre (il ne sera instancié que là où il pourra être initialisé).
Si une ou plusieurs variables sont déclarées à l'intérieur d'une instruction for, comme dans :
for (int i = 0;
elles ne sont utilisables que dans celle ci, y compris le bloc itéré, et disparaissent immédiatement après (quoique cela puisse éventuellement être configuré).
Les paramètres des fonctions présentent des nouveautés par rapport au C. Il y a des passages de paramètres par référence notés & : c'est un peu comme passer l'adresse de la variable (par *) transmise à la fonction, sauf que cela est implicite lors de l'appel. Ils peuvent aussi avoir une valeur par défaut : c'est elle qui sera utilisée si le paramètre est omis.
En C++, le mot prédéfini const peut qualifier une fonction faisant partie d'une classe (méthode). Dans ce cas, celle ci ne pourra pas modifier les variables se trouvant dans l'objet (ex : simple test comme pour ==).
Vous avez peut-être remarqué que des opérateurs comme * ou *= ont comme type de leur valeur de retour soit Verylong soit Verylong&. Dans le premier cas un Verylong est créé par recopie ; dans le second une référence sur un Verylong est renvoyée, ce qui n'est pas possible avec un objet temporaire qui disparaîtra à la sortie même de la fonction.
Si j'ai placé des (ushort) à différents endroits du code, c'est simplement pour éviter de trop nombreux messages warnings. En effet, comme ils sont multipliés par les n formats utilisés de Verylong (<4>, <6>, etc.) le compilateur peut s'arrêter selon un nombre d'avertissements configuré.
Voici le programme que j'ai écrit pour tester verylong.h :
#include <stdio.h> #include <string.h> // Placé ici pour ne pas créer un fichier de plus class Message { // Il peut le faire ! char *Texte; // Normalement #include <message.h> dans verylong.h void Recuperer(const char * Txt); public: Message(const char* Txt) { Recuperer(Txt); }; Message(const Message& Me) { Recuperer(Me.Texte); }; ~Message() { delete[] Texte; }; void Afficher() const { printf("Exception = %s\n",Texte); }; }; #include <verylong.h> #define NB_32 (32/SZ_CELL) #define NB_48 (48/SZ_CELL) #define NB_64 (64/SZ_CELL) #define NB_80 (80/SZ_CELL) #define NB_128 (128/SZ_CELL) void Numero_1(); void Numero_2(); void Numero_3(); void Numero_4(); void Numero_5(); void Numero_6(); void main() { try { bool Fini; do { int n; Fini = true; printf("Numero du test (1 - 6) : "); scanf("%d",&n); switch(n) { case 1: Numero_1(); break; case 2: Numero_2(); break; case 3: Numero_3(); break; case 4: Numero_4(); break; case 5: Numero_5(); break; case 6: Numero_6(); break; default: Fini = false; break; } } while (!Fini); } catch(Message Me) { Me.Afficher(); }; } void Numero_1() { Verylong<NB_80> a = 1L,b = 123L, c = 876L, Lim_B = 1L; Verylong<NB_80> Lim_H = "7000000000000000000000"; // Definie en lisant le // dernier nombre affiché // avant overflow do { a++; a *= b; a += c; printf("%s\n",a.Formater()); } while (a < Lim_H); do { a -= c; a /= b; a--; printf("%s\n",a.Formater()); } while (a > Lim_B); } void Numero_2() { Verylong<NB_64> a = 1L, b = 8L; while(true) { a *= b; printf("%s\n",a.Formater()); } } void Numero_3() { Verylong<NB_48> a, b = "20000000000000", Lim = "140000000000000"; while (a < Lim) { a += b; printf("%s\n",a.Formater()); } while (true) { a -= b; printf("%s\n",a.Formater()); } } void Numero_4() { char Init[50] = "",Trav[5]; int i = 0; while (true) { sprintf(Trav,"%d",(++i % 10)); strcat(Init,Trav); Verylong<NB_64> a = Init; printf("%s\n",a.Formater()); } } #define DECOUPE(l) { (l) >> 16, (ushort)(l) } void Numero_5() { ushort Tab1[] = DECOUPE(-10L), Tab2[] = DECOUPE(125000L); char Buffer[100]; Verylong<NB_64> a(Tab1,2), b(Tab2,2); printf("%s\n%s\n",a.Formater(), b.Formater(Buffer)); a.abs().Affichage(); putchar('\n'); b.abs().Affichage(); putchar('\n'); Verylong<NB_32> x = 0x80000001, y(0L,false), z; y = x; z = -y; printf("Negation de %s = %s\n",x.Formater(), z.Formater(Buffer)); ushort Tab3[] = { 0, 0, 0, 32000 }; Verylong<NB_32> c(Tab3,4); } void Numero_6() { char Buffer[100]; Verylong<NB_64> a = 1L, b = 2L, c = -5L, d = -8L; printf("%s %s %s\n",a.Formater(),(a > b ? ">" : "<=" ),b.Formater(Buffer)); printf("%s %s %s\n",a.Formater(),(a == b ? "==" : "!=" ),b.Formater(Buffer)); printf("%s %s %s\n",c.Formater(),(c > d ? ">" : "<=" ),d.Formater(Buffer)); printf("%s %s %s\n",c.Formater(),(c == d ? "==" : "!=" ),d.Formater(Buffer)); printf("%s %s %s\n",b.Formater(),(b == b ? "==" : "!=" ),b.Formater(Buffer)); Verylong<NB_64> C(0L,false), D(0L,false); C = c; D = d; printf("%s %s %s\n",C.Formater(),(C > D ? ">" : "<=" ),D.Formater(Buffer)); printf("%s %s %s\n",C.Formater(),(C > c ? ">" : "<=" ),c.Formater(Buffer)); printf("%s %s %s\n",c.Formater(),(c > C ? ">" : "<=" ),C.Formater(Buffer)); Verylong<NB_32> x = -1L, y = -100000L; printf("%s %s %s\n",x.Formater(),(x > y ? ">" : "<=" ),y.Formater(Buffer)); } void Message::Recuperer(const char* Txt) { const char* Donnee = (Txt != NULL) ? Txt : "Anomalie !!!"; Texte = new char[strlen(Donnee) + 1]; strcpy(Texte,Donnee); }
Il n'est pas vraiment exhaustif mais j'ai aussi adapté à Verylong un autre programme utilisant le type long et les ai fait fonctionner en parallèle. Si vous trouvez des anomalies dans ce template, veuillez me les signaler en me décrivant précisément la nature du problème.