Une autre erreur est due a la representation des nombres negatifs. En effet, le signe ne peut etre, lui aussi, represente que par 0 ou 1. Le codage choisi (pour les entiers 16 bits) fait que l'ajout de 1 a 32767 (le plus grand entier signe) donne -32768 (cela devait de toute facon donner un nombre car soit il y a du courant, soit il n'y en a pas, il n'est pas prevu de combinaison de 0 et 1 representant une erreur). La plupart des compilateurs ne signalent pas d'erreur en cas de depassement de capacite de nombres entiers.
Autre probleme, la codification des reels. Representer la presence ou non d'une virgule par un 0 ou un 1 est evidement impossible (comment la reconnaitre ?), la premiere solution envisagee etait donc la virgule fixe (un mot pour la partie entiere, un autre pour la partie fractionnaire). On utilise desormais la "virgule flottante" (d'ou le nom de flottants ou float), representee par un mot pour la mantisse (qui est un reel en virgule fixe puisque pas de partie entiere), un autre (souvent de taille differente) pour l'exposant (entier signe). Ceci implique deux limitations : le nombre de chiffres significatifs (du a la taille de la mantisse), et le plus grand reel codifiable (du a la taille de l'exposant, par exemple 2127=1,7.1038). On cherchera donc a ne combiner que des reels du meme ordre de grandeur. Par exemple en mecanique, ajouter a une dimension d'un metre une dilatation thermique d'un micron n'a de sens que si les flottants possedent plus de 6 chiffres significatifs, donc par exemple un algorithme cumulatif ne donnera pas de resultat si le pas en temperature est trop faible.
Dans les deux cas (virgule fixe ou flottante), un probleme se pose : en fait, nous ne pouvons representer qu'un sous ensemble des reels (je ne pense pas que les mathematiciens lui aient donne un nom) qui correspond a la difference, en base 10, entre D et R. En fait on ne peut representer que les nombres pouvant s'ecrire sous forme d'une partie fractionnaire comportant un nombre fini de chiffres (en binaire). Or, comme c'est impossible en base 10 pour 1/3 (qui pourtant s'ecrit 0,1 en base 3) la representation en binaire de 1/10 donne une suite infinie, qui est donc toujours tronquee (0,1d = 0,000100100100100100100...b). donc sur tout ordinateur calculant en binaire, (1/10)*10 donne 0,999999999... Cette erreur devient genante dans le cas ou le resultat du calcul precedent est utilise dans le calcul suivant, pour de grandes suites de calculs.
exemple (flottant):
#include <stdio.h>
void main(void)
{
float x=1000;
int i;
for (i=0;i<10000;i++)x+=0.1;
printf("On obtient %12.4f au lieu de 2000.0000\n",x);
}
Ce probleme est moins flagrant en C que dans les autres
langages, le C effectuant toujours les calculs sur des reels en double
precision. Il en resulte neanmoins que, par exemple, il ne
faut pas tester dans un programme si le resultat d'un calcul flottant
est nul mais si sa valeur absolue est inferieure a un petit
nombre. On cherchera aussi, autant que possible, a choisir des
algorithmes utilisant des entiers plutot que des reels, d'autant
plus que les calculs sur des flottants sont plus lents que sur des reels.
Lorsqu'une valeur a calculer C depend de n calculs intermediaires Ci (que l'on ne desire pas memoriser), la methode la plus simple consiste a initialiser C a la premiere valeur C1, puis pour i variant de 2 a n, calculer chaque Ci et le cumuler a C. C'est en general la methode utilisee pour les calculs de sommes, produits, suites...
exemple (boucle_A) : calculer xn :
#include <stdio.h>
void main(void)
{
int n,i,x,result;
printf("entrez x et n : ");
scanf("%d %d",&x,&n);
result=x;
for(i=1;i<n;i++)result*=x;
printf("resultat : %d\n",result);
}
La fonction puissance est souvent disponible dans les langages (pow en
C), mais correspond a un calcul assez long et souvent moins
precis (xn=exp(n*ln(x)) qui donne toujours un resultat
avec une erreur de precision meme si x est entier). La
methode ci-dessus sera plus rapide soit pour n petit (quelques
multiplications d'entiers sont plus rapides qu'une exponentielle de
logarithme), soit quand toutes les puissances intermediaires doivent
etre connues. Exercice (boucle) : faire le programme calculant 2x4-3x3+x2-5x+2 pour x reel.
exemple (boucle_B) : calcul de moyenne :
#include <stdio.h>
void main(void)
{
int n=0;
float note,moy=0;
do
{
printf("entrez la note (negative pour terminer) : ");
scanf("%f",¬e);
if(note>=0)
{
moy+=note;
n++;
}
}
while(note>=0);
moy/=n;
printf("moyenne : %f\n",moy);
}
Les valeurs des notes ayant servi au calcul ne sont pas
memorisees, car toutes stockees successivement dans la
meme memoire.
Neanmoins dans un certain nombre de cas, l'emploi de la recursivite facilite la programmation. Si le temps de calcul ou la memoire utilisee sont prohibitifs pour de grands nombres de donnees, il est toujours possible de traduire un algorithme recursif en iteratif, et souvent de maniere plus efficace que le compilateur, qui est oblige de le faire (le langage machine n'est pas recursif) sans connaitre aussi bien que vous les conditions d'utilisation de l'algorithme.