Séance 6#

Objectifs

  • Nombres réels

  • Notion de précision

  • Affichage graphique

Nous avons vu à la séance précédente que Sagemath permettait de manipuler des valeurs exactes. Voyons maintenant comment il traite le calcul numérique, c’est-à-dire le calcul sur des valeurs approchées.

Représentation des nombres réels#

Création et classe parente#

Les nombres réels sont stockés avec une certaine précision dans Sagemath. On parle généralement de nombre flottant, ou à virgule flottante, pour se distinguer du concept mathématique. On verra que l’on pourra choisir la précision de stockage d’un nombre flottant, mais elle est de \(53\) bits significatifs par défaut (c’est le standard de précision double binary64, voir détails ici).

Voyons un exemple.

a = 1.2
print(a)
1.20000000000000

Dans Sagemath, un nombre flottant réel a pour classe parente RealField, le corps des nombres réels. Ce corps est disponible dans la variable RR.

print(parent(a))
print(RR)
print(RealField())
Real Field with 53 bits of precision
Real Field with 53 bits of precision
Real Field with 53 bits of precision

On observe que l’affichage ci-dessus donne la précision de stockage. Voyons ce que cela signifie concrètement.

Virgule flottante#

Pour comprendre cela, il faut s’intéresser à la manière de représenter un nombre en binaire. Si le nombre est un entier \(n \in \mathbb{N}\), on a la représentation binaire classique :

\[ n = \sum_{i=0}^k n_i 2^i \quad \longrightarrow \quad n \;\text{ s'écrit } \quad (n_k n_{n-1} \dots n_1 n_0)_2 \]

Par exemple, l’entier \(13\) s’écrit \((1101)_2\) en base \(2\). C’est ainsi qu’il est stocké sur une machine.

Pour les nombres qui ne sont pas des entiers, on peut choisir une représentation par virgule flottante. Commençons par un exemple. On considère le nombre décimal \(x = 18,\!625\). On peut l’écrire sous la forme d’un produit entre un entier et la puissance d’une base. En base \(10\) par exemple :

\[ x = 18625 \times 10^{-3}\,. \]

L’entier \(n = 18625\) est appelé la mantisse (ici en base \(10\)), et l’entier \(-3\) l’exposant. Pour stocker un nombre décimal quelconque, il suffit donc de stocker sa mantisse et son exposant comme deux entiers, comme par exemple ci-dessus en base \(10\).

Sur un ordinateur, en revanche, on représente des objets avec des bits. Il faut reprendre la méthode ci-dessus en base \(2\). On obtient

\[ x = 149 \times 2^{-3}\,. \]

avec en binaire

\[ \text{mantisse}(x) = (10010101)_2 \quad \text{ et} \quad \text{ exposant}(x) = (-11)_2 \]

Pour stocker le nombre \(x\) de manière exacte, il faut donc au moins \(8\) bits pour la mantisse, \(2\) bits pour l’exposant, et un bit pour le signe (\(+\) ou \(-\)).

Voyons un autre exemple : \(y = \frac{1}{3} = 0,\!3333333\dots\) La décomposition en binaire de \(y\) est infinie :

\[ y = 1/4 + 1/16 + 1/64 + \dots + 1/2^{2N} + \dots = \sum_{i=1}^{+\infty} 2^{-2i} \]

Sur une machine finie, on ne peut donc pas stocker \(y\) de manière exacte en binaire, avec la méthode précédente. Il faut donc tronquer la représentation de la mantisse à un certain ordre, qu’on appelle la précision.

En pratique, si l’on se donne une précision \(N\) pour stocker \(y\), on va donc chercher le nombre le plus proche de \(y\) dont la mantisse peut être représentée sur \(N\) bits. Par exemple, dans le cas de \(y = 1/3\) et \(N=1\), ce nombre le plus proche est \(y' = 1/4\). On stocke donc \(y = 1/3\) avec \(1\) bit de précision comme \(y' = 1 \times 2^{-2}\) :

\[ \text{mantisse}(y') = (1)_2 \quad \text{ et} \quad \text{ exposant}(x) = (-10)_2 \]

Voyons cela sur un exemple :

y = 1/3
R1 = RealField(1)     # pour créer le corps des nombres flottants à 1 bit de précision
print(R1)

y1 = R1(y)            # pour créer le nombre flottant 1/3 avec 1 bit de précision
print(y1)
Real Field with 1 bits of precision
0.25

Pour conclure, stocker des nombres avec \(53\) bits de précision signifie donc qu’on les approche par des nombres à virgule flottante dont les mantisses sont de taille maximale \(53\) bits.

Erreurs de précision#

Pour certains nombres, la mantisse est donc tronquée à certain ordre. Cela signifie que les approximations effectuées peuvent mener à des erreurs de calculs.

Exemple : ajouter une valeur très petite mais non-nulle ne modifie rien.

y = 1.0
z = y + 2**(-53)     # il faudrait 54 bits de précision pour stocker z de manière exacte
print(y)
print(z)
print(y == z)
1.00000000000000
1.00000000000000
True

Pour obtenir davantage de précision, on doit donner explicitement la précision de stockage désirée, et effectuer une coercion. Comme on l’a vu à la précédente séance, cette coercion se fait par la classe parente :

R54 = RealField(54)
print(R54)

y = R54(1)
z = y + 2**(-53)
print(y)
print(z)
print(y == z)
Real Field with 54 bits of precision
1.00000000000000
1.00000000000000
False

Remarquez que l’affichage en base \(10\) ne permet parfois pas de se rendre compte que deux nombres sont différents…

Écriture décimale de constantes#

Pour obtenir l” écriture décimale de certaines constantes classiques (\(\pi\), \(e\), etc…), on peut donc utiliser le formalisme ci-dessus.

Si on souhaite obtenir le nombre \(\pi\) avec une précision d’au moins \(100\) chiffres après la virgule, on peut donc choisir une représentation avec \(101\) chiffres décimaux de précision, soit environ \(336\) bits de précision.

R336 = RealField(336)
R336(pi)
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068

Nombres complexes flottants#

Dans Sagemath, on peut également manipuler des nombres complexes avec une précision arbitraire. Pour cela, la classe parente est ComplexField(), avec comme argument de construction optionnel la précision de stockage. Comme pour les réels, la précision par défaut est de \(53\) bits, et cette classe est prédéfinie dans la variable CC.

x = 2.0 + I
print(x)
print(parent(x))
print(x in CC)
2.00000000000000 + 1.00000000000000*I
Complex Field with 53 bits of precision
True

Affichage graphique avec Sagemath : premiers pas#

Afficher une série de points dans le plan#

Avec Sagemath, il y a plusieurs manières d’afficher une série de points dans le plan. Toutes prennent en entrée une liste L qui décrit les points du plan à affciher. Donc, à la différence de python et matplotlib.pyplot, il ne faut pas déclarer séparément la liste des abscisses et celle des ordonnées.

Les trois fonctions principales qui permettent d’afficher graphiquement une liste de points sont scatter_plot, list_plot et points. Dans ce cours, nous allons voir le fonctionnement de points.

La syntaxe est la suivante :

points(L)

L est une liste de « points » qui peuvent être décrits sous la forme de :

  1. liste \([x, y]\) composée de l’abscisse et de l’ordonnée

  2. couple \((x, y)\), c’est-à-dire tuple de taille 2, aussi composé d’une abscisse et d’une ordonnée

  3. nombre complexe \(z\),

  4. vecteur de dimension 2 (on verra cette notion plus tard)

  5. … (il y a encore d’autres possibilités) …

Voyons ce que cela donne sur un premier exemple : on souhait afficher les \(100\) premiers termes de la suite de terme général \((-1)^n/(n+1)\).

L = [ [n, (-1)**n * 1/(n+1)] for n in range(100) ]
points(L).show()
../_images/fd375a159fe55d5263b1754edb9076f41156575392c6e93fa648d57e45ea8e9b.png

Second exemple : afficher des nombres complexes sur le cercle unité, espacés par \(10\) degrés \(= \pi/36\) radians.

C = [ exp(2*k*I*pi/36) for k in range(36) ]
points(C).show(aspect_ratio=1)
../_images/32b90cc6d5a41cb5df0eb001a2d5ff0fb952c8cb823c403fc9e8c773f9c3f8ea.png

Il y a de nombreuses options pour améliorer l’affichage. En voici quelques unes (ce n’est pas à retenir).

Option (exemple)

Description

xmin=2 et xmax=5

Précise que l’affichage doit commencer à \(x=2\) (à gauche) et terminer à \(x=5\) (à droite)

ymin=-1 et ymax=7

Précise que l’affichage doit commencer à \(y=-1\) (en bas) et terminer à \(y=7\) (en haut)

color="red"

Précise que les points doivent s’afficher en rouge

scale="loglog"

Produit un affichage en double échelle logarithmique

frame=True

Rajoute une grille à l’affichage

axes=False

Retire les axes de l’affichage

size=20

Affiche les points avec un diamètre de \(20\)pts (unité graphique de base)

En général, ces options sont à ajouter comme arguments optionnels des fonctions points, list_plot et scatter_plot.

P = points(L, color="red", frame=True, size=20, axes=False, ymin=-0.2, ymax=0.2)
P.show()
../_images/4e897c276bcc21e1f0313cbfa34bf75d754e54bfcd4dc562b6f0303ffbe91a61.png

Afficher une fonction prédéfinie#

Sagemath permet également d’afficher une fonction réelle prédéfinie sans avoir à précalculer de liste de points. Pour cela, on peut très simplement utiliser la fonction plot.

Exemple avec la fonction exponentielle :

plot(exp)
../_images/d58a7786007a6545fdd87075011c46a39ccb290f26f44cdfdc36a58ff154cb19.png

On peut bien entendu recadrer l’affichage :

plot(exp, xmin=-3, xmax=3)
../_images/c9c718e9ee43ca19f37227e1fcb6ac898be4cf6f0c3ee44c64444bfe3a3bfccc.png

Pour afficher une fonction d’une variable réelle « \(t\) », il faut prédéfinir cette variable abstraite. Pour cela, on utilise le mot-clef var d’une manière peu conforme : l’instruction var('t') va déclarer dans Sagemath une variable t abstraite.

var('t')
print(t)
t

Note

La variable x est déjà prédéfinie pas défaut dans Sagemath, mais il n’est pas coûteux de réécrire var('x'), notamment dans le cas où vous auriez précédent utilisé le nom x pour d’autres types de calculs.

Cela nous permet de créer des fonctions que nous pourrons ensuite afficher. Par exemple :

f = t^2 - t
plot(f, xmin=-2, xmax=2)
../_images/c7c1252f405c4e8c1d70a5d836d77bec5a09ff04376da7498781d19720c11872.png

Affichage multiple#

Enfin, commme avec matplotlib, il est possible de superposer des graphiques. La syntaxe est différente de matplotlib, mais elle est presque plus simple : il suffit d’additionner les graphiques avec l’opérateur +. Exemple :

L = [ (i/4, sin(i/4)) for i in range(40) ]
plot(sin, xmin=0, xmax=10) + points(L, color='red', size=30)
../_images/daf119678302bd6805fd987d717d28edab07f6885db6346afb373938ffa96300.png