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 :
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 :
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
avec en binaire
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 :
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}\) :
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)
où L est une liste de « points » qui peuvent être décrits sous la forme de :
liste \([x, y]\) composée de l’abscisse et de l’ordonnée
couple \((x, y)\), c’est-à-dire tuple de taille 2, aussi composé d’une abscisse et d’une ordonnée
nombre complexe \(z\),
vecteur de dimension 2 (on verra cette notion plus tard)
… (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()
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)
Il y a de nombreuses options pour améliorer l’affichage. En voici quelques unes (ce n’est pas à retenir).
Option (exemple) |
Description |
|---|---|
|
Précise que l’affichage doit commencer à \(x=2\) (à gauche) et terminer à \(x=5\) (à droite) |
|
Précise que l’affichage doit commencer à \(y=-1\) (en bas) et terminer à \(y=7\) (en haut) |
|
Précise que les points doivent s’afficher en rouge |
|
Produit un affichage en double échelle logarithmique |
|
Rajoute une grille à l’affichage |
|
Retire les axes de l’affichage |
|
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()
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)
On peut bien entendu recadrer l’affichage :
plot(exp, xmin=-3, xmax=3)
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)
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)