float
On a vu jusqu’ici comment utiliser les nombres entiers via le type int
. Cependant de nombreuses situations font appels à des nombres à virgules. Le type python qui permet de les manipuler est float
qui vient de nombres à virgules flottantes.
On peut créer de tels nombres de deux façons distinctes.
La façon la plus naturelle colle à l’utilisation courante:
>>> 1.5
1.5
>>> type(1.5)
<class 'float'>
ATTENTION notez bien que le séparateur est le point décimal .
et pas la virgule ,
!
La deuxième façon consiste à utiliser la notation scientifique avec les puissance de 10
>>> 5e-3
0.005
>>> 1.4e4
14000.0
ATTENTION ici le e
représente l’opération puissance de 10
. C’est à dire que aeb
devient a * 10 ** b
.
REMARQUE lorsqu’on a introduit les int
, on avait parlé de la division euclidienne //
. Lorsqu’on utilise une seule barre /
la division de deux int
renvoie un float
.
>>> resultat = 1 / 3
>>> type(resultat)
<class 'float'>
>>> resultat
0.3333333333333333
ATTENTION c’est là encore un endroit où l’on observe une grosse différence entre python2 et python3!
Mentionnons pour conclure que les float
offrent aussi des représentations des infinis et d’une donnée manquante:
>>> float("inf")
inf
>>> float("-inf")
-inf
>>> float("NaN")
nan
Ici inf
vient de infinite et nan
de not a number.
De nombreux nombres réels ont une écriture décimale infinie. On a vu 1 / 3
ci-dessus, mais les racines carrés, les logarithmes, les exponentielles et la trigonométrie fournissent facilement d’autres exemples. Cependant les float
ne peuvent stocker qu’un nombre finie de décimales. On a donc des erreurs d’arrondis fréquentes qui peuvent avoir des implications surprenantes.
On a ainsi attendu avant d’aborder les float
car c’est un type étonnamment compliqué. Pour s’en convaincre, on pourra aller regarder la norme IEEE 754 qui la précise.
On va se contenter pour cette partie de quelques exemples de comportements inattendus en guise d’avertissements.
>>> for n in range(50):
... if 0.1 * n != n / 10:
... print(n, end=" ")
...
3 6 7 12 14 17 19 23 24 28 29 33 34 38 39 41 46 48
On voit ici que pour de nombreux entiers entre 0
et 50
, diviser par 10
et multiplier par 0.1
ne donne pas le même résultat. Si on prend le premier nombre problématique:
>>> 0.1 * 3
0.30000000000000004
>>> 3 / 10
0.3
Un autre résultat inattendu
>>> for n in range(100):
... if 10 * (n / 10) != n:
... print(n, end=" ")
...
>>> for n in range(100):
... if 100 * (n / 100) != n:
... print(n, end=" ")
...
7 14 28 29 55 56 57 58
On pourrait ici se demander d’où proviennent les erreurs d’arrondis. Mais il faut se souvenir que les ordinateurs travaillent en binaire. On a donc des conversions base10 -> base2, puis les opérations arithmétiques, puis des conversions base2 -> base10.
A chaque étape, on peut avoir des erreurs d’arrondis. Par exemple, alors que 1/5
a une expansion décimale finie 0.2
, ce n’est pas le cas en binaire où l’expansion est 0.00110011001100110011...
. Celle-ci est donc tronquée pour être stockée en mémoire.
Un autre exemple de ce qui peut mal se passer est le suivant
>>> 0.7 + 0.1 + 0.2 == 0.7 + 0.2 + 0.1
False
>>> 0.1 + (0.2 + 0.3) == (0.1 + 0.2) + 0.3
False
avec des float
l’addition n’est ni associative ni commutative!
On prendra donc des précautions lorsqu’on manipulera des float
, plus précisément:
on ne testera jamais l’égalité exacte entre deux float
on essayera autant que possible de travailler avec des entiers le plus longtemps possible
on pourra aussi utiliser la bibliothèque fractions
pour manipuler de manière exacte des quotients
on pourra éventuellement regarder les bibliothèques decimal
et mpmath
qui permettent de travailler avec une précision arbitraire (la seconde est plus complète mais la première arrive automatiquement avec python)
On a jusqu’ici travailler séparément avec les différents types. Il s’avère qu’on a des passerelles systématiques pour passer des uns aux autres. On utilise pour cela les fonctions portant le nom des types.
On peut faire ces conversions pour les nombres:
>>> x = 1
>>> x
1
>>> type(x)
<class 'int'>
>>> y = float(x)
>>> y
1.0
>>> type(y)
<class 'float'>
>>> z = int(y)
>>> z
1
>>> type(z)
<class 'int'>
ATTENTION si on on applique int
à un flottant on tronque après la virgule:
>>> x = 1.5
>>> x
1.5
>>> type(x)
<class 'float'>
>>> y = int(x)
>>> y
1
>>> type(y)
<class 'int'>
REMARQUE si on veut plutôt un arrondi on utilise la fonction round
:
>>> round(1.7)
2
>>> int(1.7)
1
La conversion marche aussi depuis les str
pour les int
>>> nombre = "1234"
>>> type(nombre)
<class 'str'>
>>> nombre
'1234'
>>> n = int(nombre)
>>> n
1234
>>> type(n)
<class 'int'>
et pour les float
>>> flottant = "1.123"
>>> type(flottant)
<class 'str'>
>>> flottant
'1.123'
>>> x = float(flottant)
>>> x
1.123
>>> type(x)
<class 'float'>
ATTENTION il faut que les str
représente des nombres valides sous peine de plantage.
>>> int("qslkjf")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'qslkjf'
>>> int("1.123")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '1.123'
>>> float("1,1233")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: '1,1233'
On a là aussi des passerelles.
Des str
vers list
et tuple
:
>>> message = "the quick brown fox jumps over the lazy dog"
>>> message
'the quick brown fox jumps over the lazy dog'
>>> type(message)
<class 'str'>
>>> ls = list(message)
>>> ls
['t', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u', 'm', 'p', 's', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g']
>>> type(ls)
<class 'list'>
>>> ts = tuple(message)
>>> ts
('t', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u', 'm', 'p', 's', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g')
>>> type(ts)
<class 'tuple'>
De range
vers list
et tuple
:
>>> list(range(5))
[0, 1, 2, 3, 4]
>>> tuple(range(10))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
Entre list
et tuple
:
>>> ls
[1, 5, 2, 4, 3]
>>> type(ls)
<class 'list'>
>>> ts = tuple(ls)
>>> ts
(1, 5, 2, 4, 3)
>>> type(ts)
<class 'tuple'>
>>> retour = list(ts)
>>> retour
[1, 5, 2, 4, 3]
>>> type(retour)
<class 'list'>
>>> retour == ls
True
Le passage des tuple
et list
vers les str
et par contre plus délicat.
>>> message = "the quick brown fox jumps over the lazy dog"
>>> lettres = list(message)
>>> retour = "".join(lettres)
>>> retour
'the quick brown fox jumps over the lazy dog'
>>> retour == message
True
Et la fonctionnalité est en fait plus générale:
>>> message = "OUPS"
>>> lettres = tuple(message)
>>> nouveau_message = " | ".join(lettres)
>>> nouveau_message
'O | U | P | S'
>>> un = "première phrase"
>>> deux = "deuxième phrase"
>>> trois = "encore une"
>>> resultat = "\n".join((un, deux, trois))
>>> print(resultat)
première phrase
deuxième phrase
encore une
n: int
, donnez un code permettant de calculer la quantité .n
a-t-on ?On suppose qu’on a une variable message: str
, donner un code générant une variable compteur: list[tuple[str, int]]
calculant le nombre de fois que chaque caractère apparaît dans le message. Par exemple on aurait
>>> message = "OUPSS!"
>>> compteur
[('O', 1), ('U', 1), ('P', 1), ('S', 2), ('!', 1)]
On pourra obtenir la description précise de la syntaxe des flottants ici
>>> help("FLOAT")
>>> help(float)
Pour les arrondis on pourra jeter un coup d’oeil ici.
On pourra regarder les documentations des différents types pour les conversions.
>>> help(int)
>>> help(float)
>>> help(list)
>>> help(tuple)
>>> help(str)
On peut faire une simple boucle
>>> n = 1000
>>> somme = 0
>>> for k in range(1, 1 + n):
... somme = somme + 1 / k ** 2
...
>>> somme
1.6439345666815615
>>> from math import pi
>>> pi ** 2 / 6
1.6449340668482264
Les deux dernières instructions permettent de visualiser la limite lorsque n
tend vers l’infini: .
On constate que dès n=9
les erreurs d’arrondis posent problème:
>>> for n in range(15):
... calcul = (10 ** n + 10 ** (-n)) / 10 ** n
... if calcul != 1:
... print(f"OK pour n={n}")
...
OK pour n=0
OK pour n=1
OK pour n=2
OK pour n=3
OK pour n=4
OK pour n=5
OK pour n=6
OK pour n=7
OK pour n=8
>>> message = "the quick brown fox jumps over the lazy dog"
>>> compteur = list()
>>> for lettre in message:
... lettre_trouvee = False
... for indice, (caractere, compte) in enumerate(compteur):
... if lettre == caractere:
... compteur[indice] = (lettre, compte + 1)
... lettre_trouvee = True
... if not lettre_trouvee:
... compteur.append((lettre, 1))
...
>>> compteur
[('t', 2), ('h', 2), ('e', 3), (' ', 8), ('q', 1), ('u', 2), ('i', 1), ('c', 1), ('k', 1), ('b', 1), ('r', 2), ('o', 4), ('w', 1), ('n', 1), ('f', 1), ('x', 1), ('j', 1), ('m', 1), ('p', 1), ('s', 1), ('v', 1), ('l', 1), ('a', 1), ('z', 1), ('y', 1), ('d', 1), ('g', 1)]
dict
que l’on verra bientôtlettre_trouvee
qui permet de décider si lettre
est déjà présent dans compteur
et d’agir en conséquent. On parle de drapeau booléen.