floatOn 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.0ATTENTION 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.3333333333333333ATTENTION 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")
nanIci 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 48On 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.3Un 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 58On 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
Falseavec 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)
1La 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
TrueLe 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
TrueEt 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 unen: 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.6449340668482264Les 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.