From b4e74ae2978be88ca35ae983939c4ab2c9b878ae Mon Sep 17 00:00:00 2001 From: Orestis <orestis.malaspinas@pm.me> Date: Mon, 16 Oct 2023 11:08:10 +0200 Subject: [PATCH] maj 2023 --- slides/cours_4.md | 592 ------------------------- slides/cours_5.md | 1070 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1070 insertions(+), 592 deletions(-) create mode 100644 slides/cours_5.md diff --git a/slides/cours_4.md b/slides/cours_4.md index f63969a..8f06230 100644 --- a/slides/cours_4.md +++ b/slides/cours_4.md @@ -269,595 +269,3 @@ int main() { * Réusiner le code se trouvant sur [Cyberlearn](https://cyberlearn.hes-so.ch/pluginfile.php/703384/mod_resource/content/1/comprendre.c). -# Tableau à deux dimensions (1/4) - -## Mais qu'est-ce donc? - -. . . - -* Un tableau où chaque cellule est un tableau. - -## Quels cas d'utilisation? - -. . . - -* Tableau à double entrée; -* Image; -* Écran (pixels); -* Matrice (mathématique); - -# Tableau à deux dimensions (2/4) - -## Exemple: tableau à 3 lignes et 4 colonnes d'entiers - -+-----------+-----+-----+-----+-----+ -| `indices` | `0` | `1` | `2` | `3` | -+-----------+-----+-----+-----+-----+ -| `0` | `7` | `4` | `7` | `3` | -+-----------+-----+-----+-----+-----+ -| `1` | `2` | `2` | `9` | `2` | -+-----------+-----+-----+-----+-----+ -| `2` | `4` | `8` | `8` | `9` | -+-----------+-----+-----+-----+-----+ - -## Syntaxe - -```C -int tab[3][4]; // déclaration d'un tableau 4x3 -tab[2][1]; // accès à la case 2, 1 -tab[2][1] = 14; // assignation de 14 à la position 2, 1 -``` - -# Tableau à deux dimensions (3/4) - -\footnotesize - -## Exercice: - -Déclarer et initialiser aléatoirement un tableau `50x100` avec des valeurs `0` à `255` - -. . . - -```C -#define NX 50 -#define NY 100 -int tab[NX][NY]; -for (int i = 0; i < NX; ++i) { - for (int j = 0; j < NY; ++j) { - tab[i][j] = rand() % 256; // 256 niveaux de gris - } -} -``` - -## Exercice: afficher le tableau - -. . . - -```C -for (int i = 0; i < NX; ++i) { - for (int j = 0; j < NY; ++j) { - printf("%d ", tab[i][j]); - } - printf("\n"); -} -``` - -# Tableau à deux dimensions (4/4) - -## Attention - -* Les éléments ne sont **jamais** initialisés. -* Les bornes ne sont **jamais** vérifiées. - - ```C - int tab[3][2] = { {1, 2}, {3, 4}, {5, 6} }; - printf("%d\n", tab[2][1]); // affiche? - printf("%d\n", tab[10][9]); // affiche? - printf("%d\n", tab[3][1]); // affiche? - ``` - -# La couverture de la reine - -* Aux échecs la reine peut se déplacer horizontalement et verticalement -* Pour un échiquier `5x6`, elle *couvre* les cases comme ci-dessous - -+-----+-----+-----+-----+-----+-----+-----+ -| ` ` | `0` | `1` | `2` | `3` | `4` | `5` | -+-----+-----+-----+-----+-----+-----+-----+ -| `0` | `*` | ` ` | `*` | ` ` | `*` | ` ` | -+-----+-----+-----+-----+-----+-----+-----+ -| `1` | ` ` | `*` | `*` | `*` | ` ` | ` ` | -+-----+-----+-----+-----+-----+-----+-----+ -| `2` | `*` | `*` | `R` | `*` | `*` | `*` | -+-----+-----+-----+-----+-----+-----+-----+ -| `3` | ` ` | `*` | `*` | `*` | ` ` | ` ` | -+-----+-----+-----+-----+-----+-----+-----+ -| `4` | `*` | ` ` | `*` | ` ` | `*` | ` ` | -+-----+-----+-----+-----+-----+-----+-----+ - -## Exercice - -* En utilisant les conditions, les tableaux à deux dimensions, et des - `char` uniquement. -* Implémenter un programme qui demande à l'utilisateur d'entrer les - coordonnées de la reine et affiche un tableau comme ci-dessus pour un - échiquier `8x8`. - -## Poster le résultat sur `Element` - -# Types énumérés (1/2) - -* Un **type énuméré**: ensemble de *variantes* (valeurs constantes). -* En `C` les variantes sont des entiers numérotés à partir de 0. - - ```C - enum days { - monday, tuesday, wednesday, - thursday, friday, saturday, sunday - }; - ``` -* On peut aussi donner des valeurs "custom" - - ```C - enum days { - monday = 2, tuesday = 8, wednesday = -2, - thursday = 1, friday = 3, saturday = 12, sunday = 9 - }; - ``` -* S'utilise comme un type standard et un entier - - ```C - enum days d = monday; - (d + 2) == tuesday + tuesday; // true - ``` - -# Types énumérés (2/2) - -* Très utile dans les `switch ... case`{.C} - - ```C - enum days d = monday; - switch (d) { - case monday: - // trucs - break; - case tuesday: - printf("0 ou 1\n"); - break; - } - ``` -* Le compilateur vous prévient qu'il en manque! - -# Utilisation des types énumérés - -## Réusiner votre couverture de la reine avec des `enum` - -# Représentation des nombres (1/2) - -* Le nombre `247`. - -## Nombres décimaux: Les nombres en base 10 - -+--------+--------+--------+ -| $10^2$ | $10^1$ | $10^0$ | -+--------+--------+--------+ -| `2` | `4` | `7` | -+--------+--------+--------+ - -$$ -247 = 2\cdot 10^2 + 4\cdot 10^1 + 7\cdot 10^0. -$$ - -# Représentation des nombres (2/2) - -* Le nombre `11110111`. - -## Nombres binaires: Les nombres en base 2 - -+-------+-------+-------+-------+-------+-------+-------+-------+ -| $2^7$ | $2^6$ | $2^5$ | $2^4$ | $2^3$ | $2^2$ | $2^1$ | $2^0$ | -+-------+-------+-------+-------+-------+-------+-------+-------+ -| `1` | `1` | `1` | `1` | `0` | `1` | `1` | `1` | -+-------+-------+-------+-------+-------+-------+-------+-------+ - -$$ -1\cdot 2^7 + 1\cdot 2^6 +1\cdot 2^5 +1\cdot 2^4 +0\cdot 2^3 +1\cdot 2^2 -+1\cdot 2^1 +1\cdot 2^0 -$$ - -. . . - -$$ -= 247. -$$ - -# Conversion de décimal à binaire (1/2) - -## Convertir 11 en binaire? - -. . . - -* On décompose en puissances de 2 en partant de la plus grande possible - - ``` - 11 / 8 = 1, 11 % 8 = 3 - 3 / 4 = 0, 3 % 4 = 3 - 3 / 2 = 1, 3 % 2 = 1 - 1 / 1 = 1, 1 % 1 = 0 - ``` -* On a donc - - $$ - 1011 \Rightarrow 1\cdot 2^3 + 0\cdot 2^2 + 1\cdot 2^1 + 1\cdot - 2^0=11. - $$ - -# Conversion de décimal à binaire (2/2) - -## Convertir un nombre arbitraire en binaire: 247? - -* Par groupe établir un algorithme. - -. . . - -## Algorithme - -1. Initialisation - - ```C - num = 247 - while (2^N < num) { - N += 1 - } - ``` - -. . . - -2. Boucle - - ```C - while (N >= 0) { - bit = num / 2^N - num = num % 2^N - N -= 1 - } - ``` - -# Les additions en binaire - -Que donne l'addition `1101` avec `0110`? - -* L'addition est la même que dans le système décimal - - ``` - 1101 8+4+0+1 = 13 - + 0110 + 0+4+2+0 = 6 - ------- ----------------- - 10011 16+0+0+2+1 = 19 - ``` -* Les entiers sur un ordinateur ont une précision **fixée** (ici 4 bits). -* Que se passe-t-il donc ici? - -. . . - -## Dépassement de capacité: le nombre est "tronqué" - -* `10011 (19) -> 0011 (3)`. -* On fait "le tour"." - -# Entier non-signés minimal/maximal - -* Quel est l'entier non-signé maximal représentable avec 4 bit? - -. . . - -$$ -(1111)_2 = 8+4+2+1 = 15 -$$ - -* Quel est l'entier non-signé minimal représentable avec 4 bit? - -. . . - -$$ -(0000)_2 = 0+0+0+0 = 0 -$$ - -* Quel est l'entier non-signé min/max représentable avec N bit? - -. . . - -$$ -0\mbox{ et }2^N-1. -$$ - -* Donc `uint32_t?` maximal est? - -. . . - -$$ -4294967295 -$$ - - -# Les multiplications en binaire (1/2) - -Que donne la multiplication de `1101` avec `0110`? - -* L'addition est la même que dans le système décimal - - ``` - 1101 13 - * 0110 * 6 - --------- -------------- - 0000 78 - 11010 - 110100 - + 0000000 - --------- -------------- - 1001110 64+0+0+8+4+2+0 - ``` - -# Les multiplications en binaire (2/2) - -## Que fait la multiplication par 2? - -. . . - -* Décalage de un bit vers la gauche! - - ``` - 0110 - * 0010 - --------- - 0000 - + 01100 - --------- - 01100 - ``` - -. . . - -## Que fait la multiplication par $2^N$? - -. . . - -* Décalade de $N$ bits vers la gauche! - -# Entiers signés (1/2) - -Pas de nombres négatifs encore... - -## Comment faire? - -. . . - -## Solution naïve: - -* On ajoute un bit de signe (le bit de poids fort): - - ``` - 00000010: +2 - 10000010: -2 - ``` - -## Problèmes? - -. . . - -* Il y a deux zéros (pas trop grave): `10000000` et `00000000` -* Les additions différentes que pour les non-signés (très grave) - - ``` - 00000010 2 - + 10000100 + -4 - ---------- ---- - 10000110 = -6 != -2 - ``` - -# Entiers signés (2/2) - -## Beaucoup mieux - -* Complément à un: - * on inverse tous les bits: `1001 => 0110`. - -## Encore un peu mieux - -* Complément à deux: - * on inverse tous les bits, - * on ajoute 1 (on ignore les dépassements). - -. . . - -* Comment écrit-on `-4` en 8 bits? - -. . . - -``` - 4 = 00000100 - ________ - -4 => 00000100 - - 11111011 - + 00000001 - ---------- - 11111100 -``` - -# Le complément à 2 (1/2) - -## Questions: - -* Comment on écrit `+0` et `-0`? -* Comment calcule-t-on `2 + (-4)`? -* Quel est le complément à 2 de `1000 0000`? - -. . . - -## Réponses - -* Comment on écrit `+0` et `-0`? - - ``` - +0 = 00000000 - -0 = 11111111 + 00000001 = 100000000 => 00000000 - ``` -* Comment calcule-t-on `2 + (-4)`? - - ``` - 00000010 2 - + 11111100 + -4 - ---------- ----- - 11111110 -2 - ``` -* En effet - - ``` - 11111110 => 00000001 + 00000001 = 00000010 = 2. - ``` - -# Le complément à 2 (1/2) - -## Quels sont les entiers représentables en 8 bits? - -. . . - -``` -01111111 => 127 -10000000 => -128 // par définition -``` - -## Quels sont les entiers représentables sur $N$ bits? - -. . . - -$$ --2^{N-1} ... 2^{N-1}-1. -$$ - -## Remarque: dépassement de capacité en `C` - -* Comportement indéfini! - - -<!-- # TODO -- - -<!-- ## Entiers, entiers non-signés --> - -<!-- ## Complément à 1, 2 --> - -<!-- ## Nombres à virgule flottante, simple/double précision --> - -# Types composés: `struct`{.C} (1/6) - -## Fractions - -* Numérateur: `int num`; -* Dénominateur: `int denom`. - -## Addition - -```C -int num1 = 1, denom1 = 2; -int num2 = 1, denom2 = 3; -int num3 = num1 * denom2 + num2 * denom1; -int denom3 = denom1 * denom2; -``` - -## Pas super pratique.... - -# Types composés: `struct`{.C} (2/6) - -## On peut faire mieux - -* Plusieurs variables qu'on aimerait regrouper dans un seul type: `struct`{.C}. - -```C -struct fraction { // déclaration du type - int32_t num, denom; -} - -struct fraction frac; // déclaration de la variable frac -``` - -# Types composés: `struct`{.C} (3/6) - -## Simplifications - -- `typedef`{.C} permet de définir un nouveau type. - - ```C - typedef unsinged int uint; - typedef struct fraction fraction_t; - typedef struct fraction { - int32_t num, denom; - } fraction_t; - ``` -- L'initialisation peut aussi se faire avec - - ```C - fraction_t frac = {1, -2}; // num = 1, denom = -2 - fraction_t frac = {.denom = 1, .num = -2}; - fraction_t frac = {.denom = 1}; // argl! .num non initialisé - fraction_t frac2 = frac; // copie - ``` - -# Types composés: `struct`{.C} (4/6) - -## Pointeurs - -- Comme pour tout type, on peut avoir des pointeurs vers un `struct`{.C}. -- Les champs sont accessible avec le sélecteur `->`{.C} - - ```C - fraction_t *frac; // on crée un pointeur - frac->num = 1; // seg fault... - frac->denom = -1; // mémoire pas allouée. - ``` - -{width=50%} - -# Types composés: `struct`{.C} (5/6) - -## Initialisation - -- Avec le passage par **référence** on peut modifier un struct *en place*. -- Les champs sont accessible avec le sélecteur `->`{.C} - - ```C - void fraction_init(fraction_t *frac, - int32_t re, int32_t im) - { - // hypothèse: frac a déjà été allouée - frac->num = frac; - frac->denom = denom; - } - int main() { - fraction_t frac; // on alloue une fraction - fraction_init(&frac, 2, -1); // on l'initialise - } - ``` - -# Types composés: `struct`{.C} (6/6) - -## Initialisation version copie - -* On peut allouer une fraction, l'initialiser et le retourner. -* La valeur retournée peut être copiée dans une nouvelle structure. - - ```C - fraction_t fraction_create(int32_t re, int32_t im) { - fraction_t frac; - frac.num = re; - frac.denom = im; - return frac; - } - int main() { - // on crée une fraction et on l'initialise - // en copiant la fraction créé par fraction_create - // deux allocation et une copie - fraction_t frac = fraction_create(2, -1); - } - ``` diff --git a/slides/cours_5.md b/slides/cours_5.md new file mode 100644 index 0000000..4651683 --- /dev/null +++ b/slides/cours_5.md @@ -0,0 +1,1070 @@ +--- +title: "Tableaux à deux dimensions et représentation des nombres" +date: "2023-10-17" +--- + +# Réusinage de code (refactoring) + +## Exercice: + +* Réusiner le code se trouvant sur + [Cyberlearn](https://cyberlearn.hes-so.ch/pluginfile.php/703384/mod_resource/content/1/comprendre.c). + +# Tableau à deux dimensions (1/4) + +## Mais qu'est-ce donc? + +. . . + +* Un tableau où chaque cellule est un tableau. + +## Quels cas d'utilisation? + +. . . + +* Tableau à double entrée; +* Image; +* Écran (pixels); +* Matrice (mathématique); + +# Tableau à deux dimensions (2/4) + +## Exemple: tableau à 3 lignes et 4 colonnes d'entiers + ++-----------+-----+-----+-----+-----+ +| `indices` | `0` | `1` | `2` | `3` | ++-----------+-----+-----+-----+-----+ +| `0` | `7` | `4` | `7` | `3` | ++-----------+-----+-----+-----+-----+ +| `1` | `2` | `2` | `9` | `2` | ++-----------+-----+-----+-----+-----+ +| `2` | `4` | `8` | `8` | `9` | ++-----------+-----+-----+-----+-----+ + +## Syntaxe + +```C +int tab[3][4]; // déclaration d'un tableau 4x3 +tab[2][1]; // accès à la case 2, 1 +tab[2][1] = 14; // assignation de 14 à la position 2, 1 +``` + +# Tableau à deux dimensions (3/4) + +\footnotesize + +## Exercice: + +Déclarer et initialiser aléatoirement un tableau `50x100` avec des valeurs `0` à `255` + +. . . + +```C +#define NX 50 +#define NY 100 +int tab[NX][NY]; +for (int i = 0; i < NX; ++i) { + for (int j = 0; j < NY; ++j) { + tab[i][j] = rand() % 256; // 256 niveaux de gris + } +} +``` + +## Exercice: afficher le tableau + +. . . + +```C +for (int i = 0; i < NX; ++i) { + for (int j = 0; j < NY; ++j) { + printf("%d ", tab[i][j]); + } + printf("\n"); +} +``` + +# Tableau à deux dimensions (4/4) + +## Attention + +* Les éléments ne sont **jamais** initialisés. +* Les bornes ne sont **jamais** vérifiées. + + ```C + int tab[3][2] = { {1, 2}, {3, 4}, {5, 6} }; + printf("%d\n", tab[2][1]); // affiche? + printf("%d\n", tab[10][9]); // affiche? + printf("%d\n", tab[3][1]); // affiche? + ``` + +# La couverture de la reine + +* Aux échecs la reine peut se déplacer horizontalement et verticalement +* Pour un échiquier `5x6`, elle *couvre* les cases comme ci-dessous + ++-----+-----+-----+-----+-----+-----+-----+ +| ` ` | `0` | `1` | `2` | `3` | `4` | `5` | ++-----+-----+-----+-----+-----+-----+-----+ +| `0` | `*` | ` ` | `*` | ` ` | `*` | ` ` | ++-----+-----+-----+-----+-----+-----+-----+ +| `1` | ` ` | `*` | `*` | `*` | ` ` | ` ` | ++-----+-----+-----+-----+-----+-----+-----+ +| `2` | `*` | `*` | `R` | `*` | `*` | `*` | ++-----+-----+-----+-----+-----+-----+-----+ +| `3` | ` ` | `*` | `*` | `*` | ` ` | ` ` | ++-----+-----+-----+-----+-----+-----+-----+ +| `4` | `*` | ` ` | `*` | ` ` | `*` | ` ` | ++-----+-----+-----+-----+-----+-----+-----+ + +## Exercice + +* En utilisant les conditions, les tableaux à deux dimensions, et des + `char` uniquement. +* Implémenter un programme qui demande à l'utilisateur d'entrer les + coordonnées de la reine et affiche un tableau comme ci-dessus pour un + échiquier `8x8`. + +## Poster le résultat sur `Element` + +# Types énumérés (1/2) + +* Un **type énuméré**: ensemble de *variantes* (valeurs constantes). +* En `C` les variantes sont des entiers numérotés à partir de 0. + + ```C + enum days { + monday, tuesday, wednesday, + thursday, friday, saturday, sunday + }; + ``` +* On peut aussi donner des valeurs "custom" + + ```C + enum days { + monday = 2, tuesday = 8, wednesday = -2, + thursday = 1, friday = 3, saturday = 12, sunday = 9 + }; + ``` +* S'utilise comme un type standard et un entier + + ```C + enum days d = monday; + (d + 2) == monday + monday; // true + ``` + +# Types énumérés (2/2) + +* Très utile dans les `switch ... case`{.C} + + ```C + enum days d = monday; + switch (d) { + case monday: + // trucs + break; + case tuesday: + printf("0 ou 1\n"); + break; + } + ``` +* Le compilateur vous prévient qu'il en manque! + +# Utilisation des types énumérés + +## Réusiner votre couverture de la reine avec des `enum` + +A faire à la maison comme exercice! + +# Représentation des nombres (1/2) + +* Le nombre `247`. + +## Nombres décimaux: Les nombres en base 10 + ++--------+--------+--------+ +| $10^2$ | $10^1$ | $10^0$ | ++--------+--------+--------+ +| `2` | `4` | `7` | ++--------+--------+--------+ + +$$ +247 = 2\cdot 10^2 + 4\cdot 10^1 + 7\cdot 10^0. +$$ + +# Représentation des nombres (2/2) + +* Le nombre `11110111`. + +## Nombres binaires: Les nombres en base 2 + ++-------+-------+-------+-------+-------+-------+-------+-------+ +| $2^7$ | $2^6$ | $2^5$ | $2^4$ | $2^3$ | $2^2$ | $2^1$ | $2^0$ | ++-------+-------+-------+-------+-------+-------+-------+-------+ +| `1` | `1` | `1` | `1` | `0` | `1` | `1` | `1` | ++-------+-------+-------+-------+-------+-------+-------+-------+ + +$$ +1\cdot 2^7 + 1\cdot 2^6 +1\cdot 2^5 +1\cdot 2^4 +0\cdot 2^3 +1\cdot 2^2 ++1\cdot 2^1 +1\cdot 2^0 +$$ + +. . . + +$$ += 247. +$$ + +# Conversion de décimal à binaire (1/2) + +## Convertir 11 en binaire? + +. . . + +* On décompose en puissances de 2 en partant de la plus grande possible + + ``` + 11 / 8 = 1, 11 % 8 = 3 + 3 / 4 = 0, 3 % 4 = 3 + 3 / 2 = 1, 3 % 2 = 1 + 1 / 1 = 1, 1 % 1 = 0 + ``` +* On a donc + + $$ + 1011 \Rightarrow 1\cdot 2^3 + 0\cdot 2^2 + 1\cdot 2^1 + 1\cdot + 2^0=11. + $$ + +# Conversion de décimal à binaire (2/2) + +## Convertir un nombre arbitraire en binaire: 247? + +* Par groupe établir un algorithme. + +. . . + +## Algorithme + +1. Initialisation + + ```C + num = 247 + tant que (2^N < num) { + N += 1 + } + ``` + +. . . + +2. Boucle + + ```C + tant que (N >= 0) { + bit = num / 2^N + num = num % 2^N + N -= 1 + } + ``` + +# Les additions en binaire + +Que donne l'addition `1101` avec `0110`? + +* L'addition est la même que dans le système décimal + + ``` + 1101 8+4+0+1 = 13 + + 0110 + 0+4+2+0 = 6 + ------- ----------------- + 10011 16+0+0+2+1 = 19 + ``` +* Les entiers sur un ordinateur ont une précision **fixée** (ici 4 bits). +* Que se passe-t-il donc ici? + +. . . + +## Dépassement de capacité: le nombre est "tronqué" + +* `10011 (19) -> 0011 (3)`. +* On fait "le tour"." + +# Entier non-signés minimal/maximal + +* Quel est l'entier non-signé maximal représentable avec 4 bit? + +. . . + +$$ +(1111)_2 = 8+4+2+1 = 15 +$$ + +* Quel est l'entier non-signé minimal représentable avec 4 bit? + +. . . + +$$ +(0000)_2 = 0+0+0+0 = 0 +$$ + +* Quel est l'entier non-signé min/max représentable avec N bit? + +. . . + +$$ +0\mbox{ et }2^N-1. +$$ + +* Donc `uint32_t?` maximal est? + +. . . + +$$ +2^{32}-1=4'294'967'295 +$$ + + +# Les multiplications en binaire (1/2) + +Que donne la multiplication de `1101` avec `0110`? + +* La mutliplication est la même que dans le système décimal + + ``` + 1101 13 + * 0110 * 6 + --------- -------------- + 0000 78 + 11010 + 110100 + + 0000000 + --------- -------------- + 1001110 64+0+0+8+4+2+0 + ``` + +# Les multiplications en binaire (2/2) + +## Que fait la multiplication par 2? + +. . . + +* Décalage de un bit vers la gauche! + + ``` + 0110 + * 0010 + --------- + 0000 + + 01100 + --------- + 01100 + ``` + +. . . + +## Que fait la multiplication par $2^N$? + +. . . + +* Décalade de $N$ bits vers la gauche! + +# Entiers signés (1/2) + +Pas de nombres négatifs encore... + +## Comment faire? + +. . . + +## Solution naïve: + +* On ajoute un bit de signe (le bit de poids fort): + + ``` + 00000010: +2 + 10000010: -2 + ``` + +## Problèmes? + +. . . + +* Il y a deux zéros (pas trop grave): `10000000` et `00000000` +* Les additions différentes que pour les non-signés (très grave) + + ``` + 00000010 2 + + 10000100 + -4 + ---------- ---- + 10000110 = -6 != -2 + ``` + +# Entiers signés (2/2) + +## Beaucoup mieux + +* Complément à un: + * on inverse tous les bits: `1001 => 0110`. + +## Encore un peu mieux + +* Complément à deux: + * on inverse tous les bits, + * on ajoute 1 (on ignore les dépassements). + +. . . + +* Comment écrit-on `-4` en 8 bits? + +. . . + +``` + 4 = 00000100 + ________ + -4 => 00000100 + + 11111011 + + 00000001 + ---------- + 11111100 +``` + +# Le complément à 2 (1/2) + +## Questions: + +* Comment on écrit `+0` et `-0`? +* Comment calcule-t-on `2 + (-4)`? +* Quel est le complément à 2 de `1000 0000`? + +. . . + +## Réponses + +* Comment on écrit `+0` et `-0`? + + ``` + +0 = 00000000 + -0 = 11111111 + 00000001 = 100000000 => 00000000 + ``` +* Comment calcule-t-on `2 + (-4)`? + + ``` + 00000010 2 + + 11111100 + -4 + ---------- ----- + 11111110 -2 + ``` +* En effet + + ``` + 11111110 => 00000001 + 00000001 = 00000010 = 2. + ``` + +# Le complément à 2 (2/2) + +## Quels sont les entiers représentables en 8 bits? + +. . . + +``` +01111111 => 127 +10000000 => -128 // par définition +``` + +## Quels sont les entiers représentables sur $N$ bits? + +. . . + +$$ +-2^{N-1} ... 2^{N-1}-1. +$$ + +## Remarque: dépassement de capacité en `C` + +* Comportement indéfini! + +# Types composés: `struct`{.C} (1/6) + +## Fractions + +* Numérateur: `int num`; +* Dénominateur: `int denom`. + +## Addition + +```C +int num1 = 1, denom1 = 2; +int num2 = 1, denom2 = 3; +int num3 = num1 * denom2 + num2 * denom1; +int denom3 = denom1 * denom2; +``` + +## Pas super pratique.... + +# Types composés: `struct`{.C} (2/6) + +## On peut faire mieux + +* Plusieurs variables qu'on aimerait regrouper dans un seul type: `struct`{.C}. + +```C +struct fraction { // déclaration du type + int32_t num, denom; +} + +struct fraction frac; // déclaration de la variable frac +``` + +# Types composés: `struct`{.C} (3/6) + +## Simplifications + +- `typedef`{.C} permet de définir un nouveau type. + + ```C + typedef unsinged int uint; + typedef struct fraction fraction_t; + typedef struct fraction { + int32_t num, denom; + } fraction_t; + ``` +- L'initialisation peut aussi se faire avec + + ```C + fraction_t frac = {1, -2}; // num = 1, denom = -2 + fraction_t frac = {.denom = 1, .num = -2}; + fraction_t frac = {.denom = 1}; // argl! .num non initialisé + fraction_t frac2 = frac; // copie + ``` + +# Types composés: `struct`{.C} (4/6) + +## Pointeurs + +- Comme pour tout type, on peut avoir des pointeurs vers un `struct`{.C}. +- Les champs sont accessible avec le sélecteur `->`{.C} + + ```C + fraction_t *frac; // on crée un pointeur + frac->num = 1; // seg fault... + frac->denom = -1; // mémoire pas allouée. + ``` + +{width=50%} + +# Types composés: `struct`{.C} (5/6) + +## Initialisation + +- Avec le passage par **référence** on peut modifier un struct *en place*. +- Les champs sont accessible avec le sélecteur `->`{.C} + + ```C + void fraction_init(fraction_t *frac, + int32_t re, int32_t im) + { + // hypothèse: frac a déjà été allouée + frac->num = frac; + frac->denom = denom; + } + int main() { + fraction_t frac; // on alloue une fraction + fraction_init(&frac, 2, -1); // on l'initialise + } + ``` + +# Types composés: `struct`{.C} (6/6) + +## Initialisation version copie + +* On peut allouer une fraction, l'initialiser et le retourner. +* La valeur retournée peut être copiée dans une nouvelle structure. + + ```C + fraction_t fraction_create(int32_t re, int32_t im) { + fraction_t frac; + frac.num = re; + frac.denom = im; + return frac; + } + int main() { + // on crée une fraction et on l'initialise + // en copiant la fraction créé par fraction_create + // deux allocation et une copie + fraction_t frac = fraction_create(2, -1); + } + ``` + + +# Nombres à virgule (1/3) + +## Comment manipuler des nombres à virgule? + +$$ +0.1 + 0.2 = 0.3. +$$ + +Facile non? + +. . . + +## Et ça? + +```C +#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 + ++-------+-------+-------+-------+-----+----------+----------+----------+----------+ +| $2^3$ | $2^2$ | $2^1$ | $2^0$ | `.` | $2^{-1}$ | $2^{-2}$ | $2^{-3}$ | $2^{-4}$ | ++-------+-------+-------+-------+-----+----------+----------+----------+----------+ +| `1` | `0` | `1` | `0` | `.` | `0` | `1` | `0` | `1` | ++-------+-------+-------+-------+-----+----------+----------+----------+----------+ + +## Qu'est-ce ça donne en décimal? + +. . . + +$$ +2^3+2^1+\frac{1}{2^2}+\frac{1}{2^4} = 8+2+0.5+0.0625=10.5625. +$$ + +## 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 + +$$ +\underbrace{22.1214}_{\mbox{nombre}}=\underbrace{221214}_{\mbox{mantisse}}\cdot +{\underbrace{10}_{\mbox{base}}}{\overbrace{^{-4}}^{\mbox{exp.}}}, +$$ + +. . . + +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*. + +](figs/Float_example_bare.svg) + +## Spécification + +* 1 bit de signe, +* 8 bits d'exposant, +* 23 bits de mantisse. + +$$ +(-1)^{b_{31}}\cdot 2^{(b_{30}b_{29}\dots b_{23})_{2}-127}\cdot (1.b_{22}b_{21}\dots b_{0})_{2}, +$$ + +## Calculer la valeur décimale du nombre ci-dessus + +# Nombres à virgule flottante simple précision (2/4) + +](figs/Float_example.svg) + +. . . + +\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` donne `NaN`. + +# 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, soit $2^{24}-1$: + * La mantisse fait $\sim2^{24}\sim 10^7$, + * Ou encore $\sim \log_{10}(2^{24})\sim 7$. +* 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$, erreur $0.00000\overline{3}$. + * $\pi\cong3.14159$, erreur $0.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 + +$$ +(1.2)_{10} = 1.\overline{0011}\cdot 2^0\Rightarrow 0\ 01111111\ +00110011001100110011010. +$$ +Erreur d'arrondi dans les deux derniers bits et tout ceux qui viennent +ensuite +$$ +\varepsilon_2 = (00000000000000000000011)_2. +$$ +Ou en décimal +$$ +\varepsilon_{10} = 4.76837158203125\cdot 10^{-8}. +$$ + +# Précision finie (3/3) + +## Comment définir l'égalité de 2 nombres à virgule flottante? + +. . . + +Ou en d'autres termes, pour quel $\varepsilon>0$ (appelé `epsilon-machine`) on a +$$ +1+\varepsilon = 1, +$$ +pour un nombre à virgule flottante? + +. . . + +Pour un `float` (32 bits) la différence est à +$$ +2^{-23}=1.19\cdot 10^{-7}, +$$ +Soit la précision de la mantisse. + +## Comment le mesurer (par groupe)? + +. . . + +```C +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 + +$$ +(1.1)_{10}\cdot (1.1)_{10}=(1.21)_{10}=(1.2)_{10}. +$$ +En continuant ce petit jeu: +$$ +\underbrace{1.1\cdot 1.1\cdots 1.1}_{\mbox{10 fois}}=2.0. +$$ +Alors qu'en réalité +$$ +1.1^{10}=2.5937... +$$ +Soit une erreur de près de 1/5e! + +. . . + +## Le même phénomène se produit (à plus petite échelle) avec les `float` ou `double`. + +# And now for something completely different + +\Huge La récursivité + +# Exemple de récursivité (1/2) + +## La factorielle + +```C +int factorial(int n) { + if (n > 1) { + return n * factorial(n - 1); + } else { + return 1; + } +} +``` + +. . . + +## Que se passe-t-il quand on fait `factorial(4)`? + +. . . + +## 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 + +```C +int factorial(int n) { + if (n > 1) { + return n * factorial(n - 1); + } else { + return 1; + } +} +``` + +. . . + +## Que se passe-t-il quand on fait `factorial(4)`? + +. . . + +## 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? + +```C +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? + +```C +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) + +## Exercice: trouver l'$\varepsilon$-machine pour un `double` + +. . . + +Rappelez-vous vous l'avez fait en style **impératif** plus tôt. + +. . . + +```C +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? + +```C +void recurse(int n) { + printf("%d ", n % 2); + if (n / 2 != 0) { + recurse(n / 2); + } else { + printf("\n"); + } +} +recurse(13); +``` + +. . . + +```C +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! -- GitLab