1) Les fonctions
int et
math.trunc tronquent vers zéro : elles suppriment la partie décimale et gardent le signe, donc toutes deux rendent
−7.
La fonction
math.floor rend le plus grand entier inférieur ou égal à son argument ; comme
−8≤−7,5<−7, elle rend
−8.
La fonction
math.ceil rend le plus petit entier supérieur ou égal à son argument, donc
−7.
La fonction
round rend l'entier le plus proche. Ici
−7,5 est à égale distance de
−8 et de
−7, et la règle de Python est alors d'arrondir à l'entier pair : elle rend
−8. Pour la même raison,
round(-6.5) rend
−6, et non
−7.
Sur le positif
7,5, les fonctions
int,
math.trunc et
math.floor rendraient toutes
7 ; c'est sur un négatif qu'elles se séparent,
math.floor descendant d'un cran quand les deux autres remontent vers zéro. C'est donc là qu'un énoncé de concours place son cas limite, et là qu'il faut tester.
2) Ranger
1000 échantillons à raison de
24 par bac demande
⌈1000/24⌉ bacs, car
41 bacs n'en contiendraient que
984 et il resterait des échantillons à loger.
La première expression suit la lettre de cette phrase :
math.ceil(1000 / 24).
La seconde utilise l'identité
⌈x⌉=−⌊−x⌋ appliquée au quotient de deux entiers, ce qui donne
-(-1000 // 24).
Comme
1000=24×41+16, le plafond vaut
42, et les deux expressions rendent
42.
3) Notons
N=1018+1. Cet entier est impair, donc la valeur exacte cherchée est
⌈N/2⌉=5×1017+1.
C'est l'expression bâtie sur
math.ceil qui se trompe, et elle se trompe d'une unité : elle rend
500000000000000000 au lieu de
500000000000000001.
La raison tient à l'arrondi du quotient, et non à une conversion des opérandes : entre deux
int, l'opérateur
/ ne convertit ni l'un ni l'autre, il rend le flottant le plus proche du quotient
exact. Ce quotient exact vaut ici
500000000000000000,5. Or la mantisse d'un flottant tient sur
53 bits, et à cet ordre de grandeur deux flottants consécutifs sont distants de
26=64 : la demi-unité n'y est pas représentable, et l'arrondi tombe sur
5×1017. La fonction
math.ceil reçoit donc un flottant déjà entier, et n'a plus rien à relever. Le
1 final n'est pas perdu avant la division, il l'est dans l'arrondi du quotient.
Que
/ ne convertisse pas ses opérandes se voit sur un cas où la conversion serait impossible :
float(10**400) lève
OverflowError: int too large to convert to float, alors que
(10**400 + 1) / 10**400 rend
1.0. C'est ce même mécanisme qui permet, plus loin dans cette fiche, de diviser deux entiers énormes et de n'arrondir qu'au tout dernier moment.
L'expression
-(-N // 2), elle, ne quitte jamais les entiers exacts et rend la valeur juste. Dès que les données sont des entiers susceptibles de dépasser
253, le détour par le flottant est interdit.
4) Le dernier bac reçoit les échantillons restants, soit
1000mod24=16 échantillons sur
24 places. La proportion cherchée est le quotient réel
16/24, que l'on n'arrondit pas dans la variable : seul l'affichage est réglé, par le format
:.1%, qui multiplie lui-même par cent et ajoute le signe.
La ligne s'écrit
print(f"Le dernier bac est rempli a {restants / 24:.1%}."), où
restants vaut
1000 % 24.
Vérification :
import math
print(int(-7.5), math.trunc(-7.5), math.floor(-7.5), math.ceil(-7.5))
print(round(-7.5), round(-6.5))
print(math.ceil(1000 / 24), -(-1000 // 24))
grand = 10**18 + 1
print(math.ceil(grand / 2), -(-grand // 2))
restants = 1000 % 24
print(restants)
print(f"Le dernier bac est rempli a {restants / 24:.1%}.")
L'exécution affiche :
-7 -7 -8 -7
-8 -6
42 42
500000000000000000 500000000000000001
16
Le dernier bac est rempli a 66.7%.
La quatrième ligne montre l'écart d'une unité annoncé à la question 3.
À retenir : on nomme d'abord en français l'opération voulue, tronquer, plancher, plafond ou plus proche, on la contrôle sur un négatif, et dès que les données sont des entiers on reste dans les entiers avec
//.