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

+++ {"grade_id": "cell-9aacd071eecda33d", "tags": ["locked"]}

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

+++ {"grade_id": "cell-fd5a373907e22622", "tags": ["locked"]}

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

+++ {"grade_id": "cell-0749edc234488c4e", "tags": ["locked"]}

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.

+++ {"grade_id": "cell-3e4cb5925b030399", "tags": ["locked"]}

:::{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 `valeurAbsolue` 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}
:grade_id: cell-26c2f80b0554f206
:tags: [answer]

/// 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}
:grade_id: cell-7e5ba48349f32c00
:tags: [answer]

/// BEGIN SOLUTION
double factorielle(int n) {
    double r = 1;
    for (int i = 1; i <= n; i++) {
        r *= i;
    }
    return r;
}
/// END SOLUTION
```

```{code-cell}
:grade_id: cell-40ce2ba5515a4b3f
:tags: [answer]

/// BEGIN SOLUTION
double valeurAbsolue(double x) {
    if (x < 0) {
        return -x;
    }
    return x;
}
/// END SOLUTION
```

```{code-cell}
:grade_id: cell-5658ebbda24de111
:tags: [answer]

/// BEGIN SOLUTION
bool egal(double x, double y, double epsilon) {
    double v = valeurAbsolue(x-y);
    return ((v < epsilon * valeurAbsolue(x)) and (v < epsilon * valeurAbsolue(y)));
}
/// END SOLUTION
```

+++ {"grade_id": "cell-d6b02ebdc5e27637", "tags": ["locked"]}

:::{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}
:grade_id: cell-5b6857150b2f0d95
:tags: [answer]

/** 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}
:grade_id: cell-acdedc110ac3a389
:tags: [locked]

double epsilon = 0.000000001
```

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

+++ {"grade_id": "cell-83bc9f0ce83996e3", "tags": ["locked"]}

:::{admonition} Exercice 1 (suite)
Le calcul suivant devrait renvoyer $2.718 281 828 459$ (mais toutes les décimales ne
seront pas affichées) :
:::

```{code-cell}
:grade_id: cell-7c1f3a734b045e42
:tags: [locked]

expPrecision(1, epsilon)
```

+++ {"grade_id": "cell-30eafabe47f914ee", "tags": ["instructors", "locked"]}

:::{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`.
:::

+++ {"grade_id": "cell-76536f0d66f087bc", "slideshow": {"slide_type": ""}, "tags": ["locked"]}

:::{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}
:grade_id: cell-f21a4cde086edd2b
:points: 1
:tags: [test]

CHECK( valeurAbsolue( 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}
:grade_id: cell-9841d2b60ca74549
:points: 1
:tags: [test]

CHECK( valeurAbsolue(expPrecision(3, epsilon) - 20.085536923 ) < 5*epsilon )
```

```{code-cell}
:grade_id: cell-4fb64a4278d2ce62
:points: 1
:tags: [test]

CHECK( valeurAbsolue( expPrecision(5, epsilon) - 148.413159102 ) < 50*epsilon )
```

+++ {"grade_id": "cell-f14c9177d33e54ce", "points": 1, "tags": ["locked"]}

:::{admonition} Exercice 1 (suite)
3. Comparez vos résultats avec la fonction `exp` de C++ définie dans `cmath` :
:::

```{code-cell}
:grade_id: cell-2586899de62aebf4
:tags: [locked]

#include <cmath>
```

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

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

+++ {"grade_id": "cell-9b25311fb953a3d7", "tags": ["locked"]}

## 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);
```

+++ {"grade_id": "cell-44bb7d35a9655256", "tags": ["locked"]}

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