Pour répondre directement à la question, voici ma version utilisant la dénomination de la fonction R :
import math
def signif(x, digits=6):
if x == 0 or not math.isfinite(x):
return x
digits -= math.ceil(math.log10(abs(x)))
return round(x, digits)
Ma principale raison pour publier cette réponse sont les commentaires se plaignant que "0,075" arrondit à 0,07 plutôt que 0,08. Ceci est dû, comme l'a souligné "Novice C", à une combinaison d'arithmétique en virgule flottante ayant à la fois une précision finie et une représentation en base 2 . Le nombre le plus proche de 0,075 qui peut réellement être représenté est légèrement plus petit, donc l'arrondi est différent de ce à quoi vous pourriez vous attendre naïvement.
Notez également que cela s'applique à toute utilisation d'arithmétique à virgule flottante non décimale, par exemple C et Java ont tous deux le même problème.
Pour montrer plus en détail, nous demandons à Python de formater le nombre au format «hexadécimal»:
0.075.hex()
ce qui nous donne: 0x1.3333333333333p-4
. La raison en est que la représentation décimale normale implique souvent des arrondis et que ce n'est donc pas ainsi que l'ordinateur "voit" le nombre. Si vous n'êtes pas habitué à ce format, quelques références utiles sont les documents Python et le standard C .
Pour montrer comment ces chiffres fonctionnent un peu, nous pouvons revenir à notre point de départ en faisant:
0x13333333333333 / 16**13 * 2**-4
qui devrait s'imprimer 0.075
. 16**13
est parce qu'il y a 13 chiffres hexadécimaux après la virgule décimale, et2**-4
est parce que les exposants hexadécimaux sont en base-2.
Maintenant, nous avons une idée de la façon dont les flottants sont représentés, nous pouvons utiliser le decimal
module pour nous donner plus de précision, nous montrant ce qui se passe:
from decimal import Decimal
Decimal(0x13333333333333) / 16**13 / 2**4
donner: 0.07499999999999999722444243844
et, espérons-le, expliquer pourquoi round(0.075, 2)
évalue0.07