Je veux supprimer les chiffres d'un nombre flottant pour avoir un nombre fixe de chiffres après le point, comme:
1.923328437452 -> 1.923
J'ai besoin de générer une chaîne vers une autre fonction, pas d'imprimer.
Aussi, je veux ignorer les chiffres perdus, pas les contourner.
Tout d’abord, la fonction, pour ceux qui veulent juste du code copier-coller:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '{}'.format(f)
if 'e' in s or 'E' in s:
return '{0:.{1}f}'.format(f, n)
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
Ceci est valable dans Python 2.7 et 3.1+. Pour les versions plus anciennes, il n'est pas possible d'obtenir le même effet "d'arrondi intelligent" (du moins, pas sans beaucoup de code compliqué), mais arrondir à 12 décimales avant la troncature fonctionnera la plupart du temps:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '%.12f' % f
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
Le cœur de la méthode sous-jacente consiste à convertir la valeur en chaîne avec une précision absolue, puis à tout supprimer du nombre de caractères souhaité. La dernière étape est facile; cela peut être fait soit avec une manipulation de chaîne
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
ou le module decimal
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
La première étape, la conversion en chaîne, est assez difficile car il existe quelques paires de littéraux à virgule flottante (c’est-à-dire ce que vous écrivez dans le code source) qui produisent tous deux la même représentation binaire et doivent pourtant être tronqués différemment. Par exemple, considérons 0.3 et 0.29999999999999998. Si vous écrivez 0.3
dans un programme Python, le compilateur le code en utilisant le format à virgule flottante IEEE dans la séquence de bits (en supposant un flottant de 64 bits).
0011111111010011001100110011001100110011001100110011001100110011
C'est la valeur la plus proche de 0,3 qui peut être représentée avec précision comme un float IEEE. Mais si vous écrivez 0.29999999999999998
dans un programme Python, le compilateur le traduit en exactement la même valeur . Dans un cas, vous vouliez le tronquer (à un chiffre) sous la forme 0.3
, tandis que dans l'autre cas, vous vouliez le tronquer sous la forme 0.2
, mais Python ne peut que donner une réponse. C'est une limitation fondamentale de Python, voire de tout langage de programmation sans évaluation paresseuse. La fonction de troncature n'a accès qu'à la valeur binaire stockée dans la mémoire de l'ordinateur, pas à la chaîne que vous avez réellement saisie dans le code source.1
Si vous décodez la séquence de bits en un nombre décimal, en utilisant à nouveau le format à virgule flottante IEEE 64 bits, vous obtenez
0.2999999999999999888977697537484345957637...
donc une implémentation naïve aboutirait avec 0.2
même si ce n'est probablement pas ce que vous voulez. Pour plus d'informations sur l'erreur de représentation en virgule flottante, consultez le didacticiel Python =.
Il est très rare de travailler avec une valeur à virgule flottante qui est si proche d'un nombre arrondi tout en étant intentionnellement différente de ce nombre arrondi. Ainsi, lors de la troncature, il est probablement judicieux de choisir la représentation décimale "la plus belle" parmi toutes celles pouvant correspondre à la valeur en mémoire. Python 2.7 et supérieur (mais pas 3.0) inclut un algorithme sophistiqué pour faire exactement cela , auquel nous pouvons accéder via l'opération de formatage de chaîne par défaut.
'{}'.format(f)
Le seul inconvénient est que cela agit comme une spécification de format g
, en ce sens qu'elle utilise une notation exponentielle (1.23e+4
) si le nombre est suffisamment grand ou trop petit. La méthode doit donc intercepter ce cas et le traiter différemment. Il existe quelques cas où l’utilisation d’une spécification de format f
pose un problème, tel que tenter de tronquer 3e-10
à 28 chiffres de précision (cela produit 0.0000000002999999999999999980
), et je ne le suis pas. encore sûr comment mieux gérer ceux-ci.
Si vous travaillez avec float
s qui sont très proches des nombres ronds mais qui ne leur correspondent pas intentionnellement (comme 0.29999999999999998 ou 99.959999999999994), cela produira des faux positifs, c'est-à-dire qu'il arrondira les nombres que vous ne voulez pas arrondir. Dans ce cas, la solution consiste à spécifier une précision fixe.
'{0:.{1}f}'.format(f, sys.float_info.Dig + n + 2)
Le nombre de chiffres de précision à utiliser ici n'a pas vraiment d'importance, il doit simplement être suffisamment grand pour garantir que tout arrondi effectué dans la conversion de chaîne ne "gonfle" pas la valeur à sa représentation décimale de Nice. Je pense que sys.float_info.Dig + n + 2
peut être suffisant dans tous les cas, mais sinon, il faudrait peut-être augmenter 2
, et cela ne fait pas de mal de le faire.
Dans les versions antérieures de Python (jusqu'à la version 2.6 ou 3.0), le formatage des nombres en virgule flottante était beaucoup plus grossier et produisait régulièrement des éléments tels que
>>> 1.1
1.1000000000000001
Si tel est votre cas, si voulez utiliser des représentations décimales "sympas" pour la troncature, tout ce que vous pouvez faire (à ma connaissance) consiste à prendre un certain nombre de chiffres, inférieure à la précision complète représentable par un float
, et arrondissez le nombre à autant de chiffres avant de le tronquer. Un choix typique est 12,
'%.12f' % f
mais vous pouvez ajuster cela en fonction des chiffres que vous utilisez.
1Eh bien ... j'ai menti. Techniquement, vous pouvez demander à Python de ré-analyser son propre code source et d'extraire la partie correspondant au premier argument transmis à la fonction de troncature. Si cet argument est un littéral à virgule flottante, vous pouvez simplement le couper d'un certain nombre de positions après le point décimal et le renvoyer. Cependant, cette stratégie ne fonctionne pas si l'argument est une variable, ce qui le rend assez inutile. Ce qui suit est présenté pour la valeur de divertissement seulement:
def trunc_introspect(f, n):
'''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
current_frame = None
caller_frame = None
s = inspect.stack()
try:
current_frame = s[0]
caller_frame = s[1]
gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
for token_type, token_string, _, _, _ in gen:
if token_type == tokenize.NAME and token_string == current_frame[3]:
next(gen) # left parenthesis
token_type, token_string, _, _, _ = next(gen) # float literal
if token_type == tokenize.NUMBER:
try:
cut_point = token_string.index('.') + n + 1
except ValueError: # no decimal in string
return token_string + '.' + '0' * n
else:
if len(token_string) < cut_point:
token_string += '0' * (cut_point - len(token_string))
return token_string[:cut_point]
else:
raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
break
finally:
del s, current_frame, caller_frame
Généraliser ceci pour gérer le cas où vous transmettez une variable semble être une cause perdue, car il vous faudrait remonter en arrière dans l'exécution du programme jusqu'à trouver le littéral à virgule flottante qui a donné sa valeur à la variable. S'il y en a même un. La plupart des variables seront initialisées à partir d'une entrée utilisateur ou d'expressions mathématiques, auquel cas la représentation binaire est tout ce qui existe.
round(1.923328437452, 3)
Voir la documentation de Python sur les types standard . Vous devrez faire défiler un peu pour accéder à la fonction round. Le deuxième nombre indique essentiellement le nombre de décimales à arrondir.
Le résultat de round
est un float, alors faites attention (l'exemple provient de Python 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
Vous serez mieux à l'aide d'une chaîne formatée:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
n = 1.923328437452
str(n)[:4]
A mon invite Python 2.7:
>>> int(1.923328437452 * 1000)/1000.0
1.923
def trunc(num, digits):
sp = str(num).split('.')
return '.'.join([sp[0], sp[1][:digits]])
Cela devrait marcher. Il devrait vous donner la troncature que vous recherchez.
La vraie façon de le faire est de le faire
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
Script python simple -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
Tant de réponses à cette question sont tout à fait fausses. Ils arrondissent les flottants (plutôt que de les tronquer) ou ne fonctionnent pas dans tous les cas.
C'est le meilleur résultat de Google lorsque je recherche 'Python truncate float', un concept très simple qui mérite de meilleures réponses. Je conviens avec Hatchkins que l’utilisation du module decimal
est la méthode pythonique utilisée. C’est pourquoi je donne ici une fonction qui répond correctement à la question et qui fonctionne comme prévu dans tous les cas.
En remarque, les valeurs fractionnaires ne peuvent généralement pas être représentées exactement par des variables binaires à virgule flottante (voir ici pour une discussion à ce sujet), raison pour laquelle ma fonction renvoie une chaîne.
from decimal import Decimal, localcontext, ROUND_DOWN
def truncate(number, places):
if not isinstance(places, int):
raise ValueError("Decimal places must be an integer.")
if places < 1:
raise ValueError("Decimal places must be at least 1.")
# If you want to truncate to 0 decimal places, just do int(number).
with localcontext() as context:
context.rounding = ROUND_DOWN
exponent = Decimal(str(10 ** - places))
return Decimal(str(number)).quantize(exponent).to_eng_string()
J'ai fait quelque chose comme ça:
from math import trunc
def truncate(number, decimals=0):
if decimals < 0:
raise ValueError('truncate received an invalid value of decimals ({})'.format(decimals))
Elif decimals == 0:
return trunc(number)
else:
factor = float(10**decimals)
return trunc(number*factor)/factor
Tu peux faire:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
essai:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
Si vous avez envie de mathémagique, cela fonctionne pour les numéros + ve:
>>> v = 1.923328437452
>>> v - v % 1e-3
1.923
utilisez numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
int (16.5); cela donnera une valeur entière de 16, c'est-à-dire trunc, ne pourra pas spécifier de décimales, mais supposez que vous pouvez le faire en
import math;
def trunc(invalue, digits):
return int(invalue*math.pow(10,digits))/math.pow(10,digits);
Une fonction générale et simple à utiliser:
def truncate_float(number, length):
"""Truncate float numbers, up to the number specified
in length that must be an integer"""
number = number * pow(10, length)
number = int(number)
number = float(number)
number /= pow(10, length)
return number
def trunc(f,n):
return ('%.16f' % f)[:(n-16)]
Je voulais juste mentionner que le vieux tour "make round () with floor ()"
round(f) = floor(f+0.5)
peut être retourné pour faire le plancher () du tour ()
floor(f) = round(f-0.5)
Bien que ces deux règles séparent les nombres négatifs, son utilisation n’est donc pas idéale:
def trunc(f, n):
if f > 0:
return "%.*f" % (n, (f - 0.5*10**-n))
Elif f == 0:
return "%.*f" % (n, f)
Elif f < 0:
return "%.*f" % (n, (f + 0.5*10**-n))
def precision(value, precision):
"""
param: value: takes a float
param: precision: int, number of decimal places
returns a float
"""
x = 10.0**precision
num = int(value * x)/ x
return num
precision(1.923328437452, 3)
1,923
>>> sol ((1.23658945) * 10 ** 4)/10 ** 4
1.2365
# diviser et multiplier par 10 ** nombre de chiffres souhaités
Variante courte et facile
def truncate_float(value, digits_after_point=2):
pow_10 = 10 ** digits_after_point
return (float(int(value * pow_10))) / pow_10
>>> truncate_float(1.14333, 2)
>>> 1.14
>>> truncate_float(1.14777, 2)
>>> 1.14
>>> truncate_float(1.14777, 4)
>>> 1.1477
L'idée centrale donnée ici me semble être la meilleure approche pour résoudre ce problème. Malheureusement, il a reçu moins de votes alors que le réponse ultérieure qui a plus de votes n'est pas complet (comme observé dans les commentaires). Espérons que la mise en oeuvre ci-dessous fournit une solution complète et complète pour troncation.
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return (l[0]+'.'+l[1][:digits])
qui devrait prendre en charge tous les cas de coin trouvés ici et ici .
Voici un moyen facile:
def truncate(num, res=3):
return (floor(num*pow(10, res)+0.5))/pow(10, res)
pour num = 1.923328437452, cette sortie 1.923
Lorsque vous utilisez un pandas df, cela a fonctionné pour moi
import math
def truncate(number, digits) -> float:
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
df['trunc'] = df['float_val'].apply(lambda x: truncate(x,1))
df['trunc']=df['trunc'].map('{:.1f}'.format)
Il existe une solution de contournement facile dans python 3. Où couper J'ai défini avec une variable d'aide decPlace afin de faciliter son adaptation.
f = 1.12345
decPlace= 4
f_cut = int(f * 10**decPlace) /10**decPlace
Sortie:
f = 1.1234
J'espère que ça aide.
Quelque chose d'assez simple pour tenir dans une liste de compréhension, sans bibliothèques ni autres dépendances externes. Pour Python> = 3.6, écrire avec des chaînes de caractères est très simple.
L'idée est de laisser la conversion de chaîne arrondir le à un endroit de plus que nécessaire, puis couper le dernier chiffre.
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1] for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.800', '0.888', '1.125', '1.250', '1.500']
Bien sûr, il y a est arrondi qui se passe ici (notamment pour le quatrième chiffre), mais arrondir à un moment donné est inévitable. Si la transition entre la troncature et l'arrondi est pertinente, voici un exemple légèrement meilleur:
>>> nacc = 6 # desired accuracy (maximum 15!)
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nacc}f}'[:-(nacc-nout)] for x in [2.9999, 2.99999, 2.999999, 2.9999999]]
>>> ['2.999', '2.999', '2.999', '3.000']
Bonus: supprimer les zéros à droite
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1].rstrip('0') for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.8', '0.888', '1.125', '1.25', '1.5']
La plupart des réponses sont bien trop compliquées à mon avis, qu'en est-il de cela?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Il suffit de rechercher l’index de '.' et tronquer comme vous le souhaitez (pas d'arrondi). Convertir la chaîne en float comme dernière étape.
Ou dans votre cas si vous obtenez un float en entrée et souhaitez une chaîne en sortie:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output