-
orestis.malaspin authoredorestis.malaspin authored
- Nombres à virgule (1/3)
- Comment manipuler des nombres à virgule?
- Et ça?
- Que se passe-t-il donc?
- Nombres à virgule (2/3)
- Nombres à virgule fixe
- Qu'est-ce ça donne en décimal?
- Limites de cette représentation?
- Nombres à virgule (3/3)
- Nombres à virgule fixe
- Solution partielle?
- Nombres à virgule flottante (1/2)
- Notation scientifique
- Nombres à virgule flottante (2/2)
- Quel est l'avantage?
- Différence fondamentale avec la virgule fixe?
- Quel inconvénient y a-t-il?
- Nombres à virgule flottante simple précision (1/4)
- Spécification
- Calculer la valeur décimale du nombre ci-dessus
- Nombres à virgule flottante simple précision (2/4)
- Nombres à virgule flottante simple précision (3/4)
- Quel nombre ne peux pas être vraiment représenté?
- Zéro: exception pour l'exposant
- Nombres à virgule flottante simple précision (4/4)
- Quels sont les plus petits/grands nombres positifs représentables?
- Combien de chiffres significatifs en décimal?
- Nombres à virgule flottante double précision (64bits)
- Spécification
- Combien de chiffres significatifs?
- Plus petit/plus grand nombre représentable?
- Précision finie (1/3)
- Erreur de représentation
- Et quand on calcule?
- Même pas associatif!
- Précision finie (2/3)
- Erreur de représentation virgule flottante
- Précision finie (3/3)
- Comment définir l'égalité de 2 nombres à virgule flottante?
- Comment le mesurer (par groupe)?
- Erreurs d'arrondi
- Multiplication avec deux chiffres significatifs, décimal
- Le même phénomène se produit (à plus petite échelle) avec les float ou double.
- And now for something completely different
- La factorielle: Code impératif
- Exemple de récursivité (1/2)
- La factorielle
- Que se passe-t-il quand on fait factorial(4)?
- On empile les appels
- Exemple de récursivité (2/2)
- La factorielle
- Que se passe-t-il quand on fait factorial(4)?
- On dépile les calculs
- La récursivité (1/4)
- Formellement
- Pour la factorielle, qui est qui?
- La récursivité (2/4)
- Formellement
- Pour la factorielle, qui est qui?
- La récursivité (3/4)
- Exercice: trouver l'\varepsilon-machine pour un double
- La récursivité (4/4)
- Exercice: que fait ce code récursif?
- Exercice: réusinage et récursivité (1/4)
- Réusiner le code du PGCD avec une fonction récursive
- Étudier l'exécution
- Exercice: réusinage et récursivité (2/4)
- Réusiner le code du PGCD avec une fonction récursive
- Étudier l'exécution
- Exercice: réusinage et récursivité (3/4)
- Réusiner le code du PGCD avec une fonction récursive
- Étudier l'exécution
- Effectuer l'empilage - dépilage
- Exercice: réusinage et récursivité (4/4)
- Écrire le code
- La suite de Fibonacci (1/2)
- Règle
- Exercice: écrire la fonction \mathrm{Fib} en récursif et impératif
- En récursif (6 lignes)
- La suite de Fibonacci (2/2)
- Et en impératif (11 lignes)
title: "Récursivité"
date: "2023-10-31"
Nombres à virgule (1/3)
Comment manipuler des nombres à virgule?
Facile non?
. . .
Et ça?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
float a = atof(argv[1]);
float b = atof(argv[2]);
printf("%.10f\n", (double)(a + b));
}
. . .
Que se passe-t-il donc?
Nombres à virgule (2/3)
Nombres à virgule fixe
+-------+-------+-------+-------+-----+----------+----------+----------+----------+ |
.
| 1
| 0
| 1
| 0
| .
| 0
| 1
| 0
| 1
|
+-------+-------+-------+-------+-----+----------+----------+----------+----------+
Qu'est-ce ça donne en décimal?
. . .
Limites de cette représentation?
. . .
- Tous les nombres
> 16
. - Tous les nombres
< 0.0625
. - Tous les nombres dont la décimale est pas un multiple de
0.0625
.
Nombres à virgule (3/3)
Nombres à virgule fixe
- Nombres de 0=0000.0000à15.9375=1111.1111.
- Beaucoup de "trous" (au moins 0.0625) entre deux nombres.
Solution partielle?
. . .
- Rajouter des bits.
- Bouger la virgule.
Nombres à virgule flottante (1/2)
Notation scientifique
- Les nombres sont représentés en terme:
- Une mantisse
- Une base
- Un exposant
. . .
On peut donc séparer la représentation en 2:
- La mantisse
- L'exposant
Nombres à virgule flottante (2/2)
Quel est l'avantage?
. . .
On peut représenter des nombres sur énormément d'ordres de grandeur avec un nombre de bits fixés.
Différence fondamentale avec la virgule fixe?
. . .
La précision des nombres est variable:
- On a uniquement un nombre de chiffres significatifs.
123456\cdot 10^{23}+ 123456\cdot 10^{-23}.
Quel inconvénient y a-t-il?
. . .
Ce mélange d'échelles entraîne un perte de précision.
Nombres à virgule flottante simple précision (1/4)
Aussi appelés IEEE 754 single-precision binary floating point.
Spécification
- 1 bit de signe,
- 8 bits d'exposant,
- 23 bits de mantisse.
Calculer la valeur décimale du nombre ci-dessus
Nombres à virgule flottante simple précision (2/4)
. . .
\begin{align} \mbox{exposant}&=\sum_{i=0}^7 b_{23+i}2^i=2^2+2^3+2^4+2^5+2^6=124-127,\ \mbox{mantisse}&=1+\sum_{i=1}^{23}b_{23-i}2^{-i}=1+2^{-2}=1.25,\ &\Rightarrow (-1)^0\cdot 2^{-3}\cdot 1.25=0.15625 \end{align}
Nombres à virgule flottante simple précision (3/4)
Quel nombre ne peux pas être vraiment représenté?
. . .
Zéro: exception pour l'exposant
- Si l'exposant est
00000000
(zéro)(-1)^{\mbox{sign}}\cdot 2^{-126}\cdot 0.\mbox{mantisse}, - Sinon si l'exposant est
00000001
à11111110
\mbox{valeur normale}, - Sinon
11111111
donneNaN
.
Nombres à virgule flottante simple précision (4/4)
Quels sont les plus petits/grands nombres positifs représentables?
. . .
\begin{align} 0\ 0\dots0\ 0\dots01&=2^{-126}\cdot 2^{-23}=1.4...\cdot 10^{-45},\ 0\ 1\dots10\ 1\dots1&=2^{127}\cdot (2-2^{-23})=3.4...\cdot 10^{38}. \end{align}
Combien de chiffres significatifs en décimal?
. . .
- 24 bits (23 + 1) sont utiles pour la mantisse, soit2^{24}-1:
- La mantisse fait \sim2^{24}\sim 10^7,
- Ou encore \sim \log_{10}(2^{24})\sim 7.
- La mantisse fait
- Environ sept chiffres significatifs.
Nombres à virgule flottante double précision (64bits)
Spécification
- 1 bit de signe,
- 11 bits d'exposant,
- 52 bits de mantisse.
. . .
Combien de chiffres significatifs?
- La mantisse fait \sim 2^{53}\sim10^{16},
- Ou encore \sim \log_{10}(2^{53})\sim 16,
- Environ seize chiffres significatifs.
Plus petit/plus grand nombre représentable?
. . .
- Plus petite mantisse et exposant: \sim 2^{-52}\cdot 2^{-1022}\sim 4\cdot 10^{-324},
- Plus grande mantisse et exposant: \sim 2\cdot 2^{1023}\sim \cdot 1.8\cdot 10^{308}.
Précision finie (1/3)
Erreur de représentation
- Les nombres réels ont potentiellement un nombre infini de décimales
-
1/3=0.\overline{3},
-
\pi=3.1415926535....
-
- Les nombres à virgule flottante peuvent en représenter qu'un nombre
fini.
-
1/3\cong 0.33333, erreur0.00000\overline{3}.
-
\pi\cong3.14159, erreur0.0000026535....
-
On rencontre donc des erreurs de représentation ou erreurs d'arrondi.
. . .
Et quand on calcule?
- Avec deux chiffres significatifs \begin{align} &8.9+(0.02+0.04)=8.96=9.0,\ &(8.9+0.02)+0.04=8.9+0.04=8.9. \end{align}
. . .
Même pas associatif!
Précision finie (2/3)
Erreur de représentation virgule flottante
Précision finie (3/3)
Comment définir l'égalité de 2 nombres à virgule flottante?
. . .
Ou en d'autres termes, pour quel
epsilon-machine
) on a
. . .
Pour un float
(32 bits) la différence est à
Comment le mesurer (par groupe)?
. . .
float eps = 1.0;
while ((float)1.0 + (float)0.5 * eps != (float)1.0) {
eps = (float)0.5 * eps;
}
printf("eps = %g\n", eps);
Erreurs d'arrondi
Et jusqu'ici on a encore pas fait d'arithmétique!
Multiplication avec deux chiffres significatifs, décimal
. . .
float
ou double
.
Le même phénomène se produit (à plus petite échelle) avec les And now for something completely different
\Huge La récursivité
La factorielle: Code impératif
- Code impératif
int factorial(int n) {
int f = 1;
for (int i = 1; i < n; ++i) {
f *= i;
}
return f;
}
Exemple de récursivité (1/2)
La factorielle
int factorial(int n) {
if (n > 1) {
return n * factorial(n - 1);
} else {
return 1;
}
}
. . .
factorial(4)
?
Que se passe-t-il quand on fait . . .
On empile les appels
+----------------+----------------+----------------+----------------+
| | | | factorial(1)
|
+----------------+----------------+----------------+----------------+
| | | factorial(2)
| factorial(2)
|
+----------------+----------------+----------------+----------------+
| | factorial(3)
| factorial(3)
| factorial(3)
|
+----------------+----------------+----------------+----------------+
| factorial(4)
| factorial(4)
| factorial(4)
| factorial(4)
|
+----------------+----------------+----------------+----------------+
Exemple de récursivité (2/2)
La factorielle
int factorial(int n) {
if (n > 1) {
return n * factorial(n - 1);
} else {
return 1;
}
}
. . .
factorial(4)
?
Que se passe-t-il quand on fait . . .
On dépile les calculs
+----------------+----------------+----------------+----------------+
| 1
| | | |
+----------------+----------------+----------------+----------------+
| factorial(2)
| 2 * 1 = 2
| | |
+----------------+----------------+----------------+----------------+
| factorial(3)
| factorial(3)
| 3 * 2 = 6
| |
+----------------+----------------+----------------+----------------+
| factorial(4)
| factorial(4)
| factorial(4)
| 4 * 6 = 24
|
+----------------+----------------+----------------+----------------+
La récursivité (1/4)
Formellement
- Une condition de récursivité - qui réduit les cas successifs vers...
- Une condition d'arrêt - qui retourne un résultat
Pour la factorielle, qui est qui?
int factorial(int n) {
if (n > 1) {
return n * factorial(n - 1);
} else {
return 1;
}
}
La récursivité (2/4)
Formellement
- Une condition de récursivité - qui réduit les cas successifs vers...
- Une condition d'arrêt - qui retourne un résultat
Pour la factorielle, qui est qui?
int factorial(int n) {
if (n > 1) { // Condition de récursivité
return n * factorial(n - 1);
} else { // Condition d'arrêt
return 1;
}
}
La récursivité (3/4)
\varepsilon-machine pour un double
Exercice: trouver l'. . .
Rappelez-vous vous l'avez fait en style impératif plus tôt.
. . .
double epsilon_machine(double eps) {
if (1.0 + eps != 1.0) {
return epsilon_machine(eps / 2.0);
} else {
return eps;
}
}
La récursivité (4/4)
\footnotesize
Exercice: que fait ce code récursif?
void recurse(int n) {
printf("%d ", n % 2);
if (n / 2 != 0) {
recurse(n / 2);
} else {
printf("\n");
}
}
recurse(13);
. . .
recurse(13): n = 13, n % 2 = 1, n / 2 = 6,
recurse(6): n = 6, n % 2 = 0, n / 2 = 3,
recurse(3): n = 3, n % 2 = 1, n / 2 = 1,
recurse(1): n = 1, n % 2 = 1, n / 2 = 0.
// affiche: 1 1 0 1
. . .
Affiche la représentation binaire d'un nombre!
Exercice: réusinage et récursivité (1/4)
Réusiner le code du PGCD avec une fonction récursive
Étudier l'exécution
42 = 27 * 1 + 15
27 = 15 * 1 + 12
15 = 12 * 1 + 3
12 = 3 * 4 + 0
Exercice: réusinage et récursivité (2/4)
Réusiner le code du PGCD avec une fonction récursive
Étudier l'exécution
42 = 27 * 1 + 15 | PGCD(42, 27)
27 = 15 * 1 + 12 | PGCD(27, 15)
15 = 12 * 1 + 3 | PGCD(15, 12)
12 = 3 * 4 + 0 | PGCD(12, 3)
Exercice: réusinage et récursivité (3/4)
Réusiner le code du PGCD avec une fonction récursive
Étudier l'exécution
42 = 27 * 1 + 15 | PGCD(42, 27)
27 = 15 * 1 + 12 | PGCD(27, 15)
15 = 12 * 1 + 3 | PGCD(15, 12)
12 = 3 * 4 + 0 | PGCD(12, 3)
Effectuer l'empilage - dépilage
. . .
PGCD(12, 3) | 3
PGCD(15, 12) | 3
PGCD(27, 15) | 3
PGCD(42, 27) | 3
Exercice: réusinage et récursivité (4/4)
Écrire le code
. . .
int pgcd(int n, int m) {
if (n % m > 0) {
return pgcd(m, n % m);
} else {
return m;
}
}
La suite de Fibonacci (1/2)
Règle
\mathrm{Fib} en récursif et impératif
Exercice: écrire la fonction . . .
En récursif (6 lignes)
int fib(int n) {
if (n > 1) {
return fib(n - 1) + fib(n - 2);
}
return n;
}
La suite de Fibonacci (2/2)
Et en impératif (11 lignes)
int fib_imp(int n) {
int fib0 = 1;
int fib1 = 1;
int fib = n == 0 ? 0 : fib1;
for (int i = 2; i < n; ++i) {
fib = fib0 + fib1;
fib0 = fib1;
fib1 = fib;
}
return fib;
}