Une autre erreur est due à la représentation des nombres négatifs. En effet, le signe ne peut être, lui aussi, représenté que par 0 ou 1. Le codage choisi (pour les entiers 16 bits) fait que l'ajout de 1 à 32767 (le plus grand entier signé) donne -32768 (cela devait de toute façon donner un nombre car soit il y a du courant, soit il n'y en a pas, il n'est pas prévu de combinaison de 0 et 1 représentant une erreur). La plupart des compilateurs ne signalent pas d'erreur en cas de dépassement de capacité de nombres entiers.
Autre problème, la codification des réels. Représenter la présence ou non d'une virgule par un 0 ou un 1 est évidement impossible (comment la reconnaître ?), la première solution envisagée était donc la virgule fixe (un mot pour la partie entière, un autre pour la partie fractionnaire). On utilise désormais la "virgule flottante" (d'où le nom de flottants ou float), représentée par un mot pour la mantisse (qui est un réel en virgule fixe puisque pas de partie entière), un autre (souvent de taille différente) pour l'exposant (entier signé). Ceci implique deux limitations : le nombre de chiffres significatifs (dû à la taille de la mantisse), et le plus grand réel codifiable (dû à la taille de l'exposant, par exemple 2127=1,7.1038). On cherchera donc à ne combiner que des réels du même ordre de grandeur. Par exemple en mécanique, ajouter à une dimension d'un mètre une dilatation thermique d'un micron n'a de sens que si les flottants possèdent plus de 6 chiffres significatifs, donc par exemple un algorithme cumulatif ne donnera pas de résultat si le pas en température est trop faible.
Dans les deux cas (virgule fixe ou flottante), un problème se pose : en fait, nous ne pouvons représenter qu'un sous ensemble des réels (je ne pense pas que les mathématiciens lui aient donné un nom) qui correspond à la différence, en base 10, entre D et R. En fait on ne peut représenter que les nombres pouvant s'écrire 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'écrit 0,1 en base 3) la représentation en binaire de 1/10 donne une suite infinie, qui est donc toujours tronquée (0,1d = 0,000100100100100100100...b). donc sur tout ordinateur calculant en binaire, (1/10)*10 donne 0,999999999... Cette erreur devient gênante dans le cas où le résultat du calcul précédent est utilisé 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 problème est moins flagrant en C que dans les autres langages, le C effectuant toujours les calculs sur des réels en double précision. Il en résulte néanmoins que, par exemple, il ne faut pas tester dans un programme si le résultat d'un calcul flottant est nul mais si sa valeur absolue est inférieure à un petit nombre. On cherchera aussi, autant que possible, à choisir des algorithmes utilisant des entiers plutôt que des réels, d'autant plus que les calculs sur des flottants sont plus lents que sur des réels.
Lorsqu'une valeur à calculer C dépend de n calculs intermédiaires Ci (que l'on ne désire pas mémoriser), la méthode la plus simple consiste à initialiser C à la première valeur C1, puis pour i variant de 2 à n, calculer chaque Ci et le cumuler à C. C'est en général la méthode utilisée 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("résultat : %d\n",result); }La fonction puissance est souvent disponible dans les langages (pow en C), mais correspond à un calcul assez long et souvent moins précis (xn=exp(n*ln(x)) qui donne toujours un résultat avec une erreur de précision même si x est entier). La méthode 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 intermédiaires doivent être connues.
Exercice (boucle) : faire le programme calculant 2x4-3x3+x2-5x+2 pour x réel.
exemple (boucle_B) : calcul de moyenne :
#include <stdio.h> void main(void) { int n=0; float note,moy=0; do { printf("entrez la note (négative 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 mémorisées, car toutes stockées successivement dans la même mémoire.
Néanmoins dans un certain nombre de cas, l'emploi de la récursivité facilite la programmation. Si le temps de calcul ou la mémoire utilisée sont prohibitifs pour de grands nombres de données, il est toujours possible de traduire un algorithme récursif en itératif, et souvent de manière plus efficace que le compilateur, qui est obligé de le faire (le langage machine n'est pas récursif) sans connaître aussi bien que vous les conditions d'utilisation de l'algorithme.