---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
kernelspec:
  display_name: C++17
  language: C++17
  name: xcpp17
---

+++ {"nbgrader": {"grade": false, "grade_id": "cell-9aacd071eecda33d", "locked": true, "schema_version": 3, "solution": false}}

# TP : implanter la fonction exponentielle (4/5)

+++ {"nbgrader": {"grade": false, "grade_id": "cell-fd5a373907e22622", "locked": true, "schema_version": 3, "solution": false}}

## Partie 4 : calcul de l'exponentielle avec une précision fixée ♣

+++ {"nbgrader": {"grade": false, "grade_id": "cell-0749edc234488c4e", "locked": true, "schema_version": 3, "solution": false}}

Dans la partie 2, vous avez défini une fonction `expRang` qui
calcule une approximation de l'exponentielle en tronquant la somme à
un certain rang décidé à l'avance. Cependant le rang nécessaire pour
obtenir une bonne précision dépend du nombre réel $x$ pour lequel on
veut calculer $e^x$. On cherche maintenant à calculer une
approximation de l'exponentielle en fixant la *précision* et non plus
le rang. Pour cela on va écrire une nouvelle fonction d'approximation
de l'exponentielle, dans laquelle le rang auquel on arrête la somme ne
sera pas décidé à l'avance, mais dépendra de l'évolution du calcul
qu'on est en train de faire.

+++ {"nbgrader": {"grade": false, "grade_id": "cell-3e4cb5925b030399", "locked": true, "schema_version": 3, "solution": false}}

:::{admonition} Exercice 1

1. copiez-collez dans les quatre cellules suivantes vos fonctions
   `puissance` et `factorielle` de la [partie 1](02-exponentielle1.md)
   ainsi que `abs` et `egal` de la [partie 3](02-exponentielle3.md);
   si vous avez déjà défini la fonction `egal_relatif`, mettez là ici
   sous le nom `egal` :

:::

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-26c2f80b0554f206
  locked: false
  schema_version: 3
  solution: true
---
/// BEGIN SOLUTION
double puissance(double x, int n) {
    double r = 1;
    for(int i = 0; i < n; i++) {
        r *= x;
    }
    return r;
}
/// END SOLUTION
```

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-7e5ba48349f32c00
  locked: false
  schema_version: 3
  solution: true
---
/// BEGIN SOLUTION
double factorielle(int n) {
    double r = 1;
    for (int i = 1; i <= n; i++) {
        r *= i;
    }
    return r;
}
/// END SOLUTION
```

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-40ce2ba5515a4b3f
  locked: false
  schema_version: 3
  solution: true
---
/// BEGIN SOLUTION
double abs(double x) {
    if (x < 0) {
        return -x;
    }
    return x;
}
/// END SOLUTION
```

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-5658ebbda24de111
  locked: false
  schema_version: 3
  solution: true
---
/// BEGIN SOLUTION
bool egal(double x, double y, double epsilon) {
    double v = abs(x-y);
    return ((v < epsilon * abs(x)) and (v < epsilon * abs(y)));
}
/// END SOLUTION
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-d6b02ebdc5e27637", "locked": true, "schema_version": 3, "solution": false}}

:::{admonition} Exercice 1 (suite)
2.  Définissez une nouvelle fonction d'approximation de l'exponentielle
    qui somme les termes $\frac{x^i}{i!}$ jusqu'à ce que le prochain
    terme à ajouter ne modifie pas la valeur de la somme, selon la
    précision donnée :
:::

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-5b6857150b2f0d95
  locked: false
  schema_version: 3
  solution: true
---
/** Calcul de la fonction exponentielle à precision fixée
 * @param x un nombre de type double
 * @param epsilon un nombre de type double
 * @return e^x avec précision epsilon
**/
double expPrecision(double x, double epsilon) {
    /// BEGIN SOLUTION
    double e1 = 0;
    double e2 = 1;
    int i = 1;
    while(not egal(e1,e2,epsilon)) {
        e1 = e2;
        e2 += puissance(x,i) / factorielle(i);
        i += 1;
    }
    return e2;
    /// END SOLUTION
}
```

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-acdedc110ac3a389
  locked: true
  schema_version: 3
  solution: false
  task: false
---
double epsilon = 0.000000001
```

```{code-cell}
epsilon = 1e-9
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-83bc9f0ce83996e3", "locked": true, "schema_version": 3, "solution": false, "task": false}}

:::{admonition} Exercice 1 (suite)
Le calcul suivant devrait renvoyer $2.718 281 828 459$ :
:::

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-7c1f3a734b045e42
  locked: true
  schema_version: 3
  solution: false
---
expPrecision(1, epsilon)
```

+++ {"tags": ["instructors"], "nbgrader": {"grade": false, "grade_id": "cell-30eafabe47f914ee", "locked": true, "schema_version": 3, "solution": false, "task": false}}

:::{admonition} Note aux enseignants
À ce stade, selon où en est l'étudiant, la fonction `expPrecision`
utilisera la version relative ou absolue de `egal`. Le test suivant
doit donc passer dans les deux cas. C'est le cas car on utilise des
valeurs de `exp` pas trop éloignées de `1`.
:::

+++ {"nbgrader": {"grade": false, "grade_id": "cell-76536f0d66f087bc", "locked": true, "schema_version": 3, "solution": false, "task": false}, "editable": true, "slideshow": {"slide_type": ""}, "tags": []}

:::{admonition} Exercice 1 (suite)
Il n'y a pas forcément suffisamment de chiffres significatifs affichés
pour le vérifier. Faisons à la place un test :
:::

```{code-cell}
---
nbgrader:
  grade: true
  grade_id: cell-f21a4cde086edd2b
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
CHECK( abs( expPrecision(1, epsilon) - 2.718281828459 ) < epsilon )
```

::::{admonition} Exercice 1 (suite)

:::{attention}

Si vous n'avez pas encore défini `egal_relatif` dans la [partie
3](02-exponentielle3.md), revenez-y et copiez votre fonction en début
de cette feuille sous le nom de `egal` avant de passer à la suite.

:::

Notre test d'arrêt ne garantit en fait pas d'obtenir une précision
relative de epsilon : même si le terme suivant est plus petit que
epsilon, l'accumulation de tous les termes suivants pourrait largement
dépasser epsilon, comme dans les exemples suivants :

::::

```{code-cell}
---
nbgrader:
  grade: true
  grade_id: cell-9841d2b60ca74549
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
CHECK( abs(expPrecision(3, epsilon) - 20.085536923 ) < 5*epsilon )
```

```{code-cell}
---
nbgrader:
  grade: true
  grade_id: cell-4fb64a4278d2ce62
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
CHECK( abs( expPrecision(5, epsilon) - 148.413159102 ) < 50*epsilon )
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-f14c9177d33e54ce", "locked": true, "points": 1, "schema_version": 3, "solution": false, "task": true}}

:::{admonition} Exercice 1 (suite)

3.  Comparez vos résultats avec la fonction `exp` de C++ définie dans `cmath` :

:::

```{code-cell}
---
nbgrader:
  grade: false
  grade_id: cell-2586899de62aebf4
  locked: true
  schema_version: 3
  solution: false
---
#include <cmath>
```

```{code-cell}
exp(5)
```

```{code-cell}
exp(3)
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-9b25311fb953a3d7", "locked": true, "schema_version": 3, "solution": false}}

## Bilan de la partie 4

Très bien, vous avez défini la fonction exponentielle à précision
fixée. Maintenant étudions sa performance en calculant son temps
d'exécution :

```{code-cell}
%timeit expPrecision(10, 0.00000001);
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-44bb7d35a9655256", "locked": true, "schema_version": 3, "solution": false}}

Dans la [partie 5](02-exponentielle5.md), vous tenterez d'optimiser
cette définition.
