Ik wil cijfers van een drijver verwijderen om een vast aantal cijfers na de punt te hebben, zoals:
1.923328437452 → 1.923
Ik moet het uitvoeren als een tekenreeks naar een andere functie, niet afdrukken.
Ik wil ook de verloren cijfers negeren, niet om ze heen.
Antwoord 1, Autoriteit 100%
Ten eerste, de functie, voor degenen die gewoon een code voor kopiëren en plakken willen:
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]])
Dit is geldig in Python 2.7 en 3.1+. Voor oudere versies is het niet mogelijk om hetzelfde “intelligent ronden” -effect te krijgen (tenminste, niet zonder veel ingewikkelde code), maar afronding tot 12 decimale plaatsen voordat de truncatie veel van de tijd zal werken:
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]])
Uitleg
De kern van de onderliggende methode is om de waarde om te zetten in een string op volledige precisie en snijdt vervolgens alles buiten het gewenste aantal tekens af. De laatste stap is eenvoudig; het kan worden gedaan met een stringmanipulatie
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
of de decimal
module
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
De eerste stap, het omzetten naar een string, is behoorlijk moeilijk omdat er enkele paren drijvende puntletterwaarden zijn (d.w.z. wat u in de broncode schrijft) die beide dezelfde binaire weergave produceren en toch anders moet worden afgekapt. Overweeg bijvoorbeeld 0,3 en 0.29999999999999998. Als u 0.3
in een Python-programma schrijft, codeert de compiler met behulp van het IEEE-drijvend-puntformaat in de reeks bits (aannemen van een 64-bits float)
0011111111010011001100110011001100110011001100110011001100110011
Dit is de dichtstbijzijnde waarde van 0,3 die nauwkeurig kan worden weergegeven als een IEEE-float. Maar als u 0.29999999999999998
in een Python-programma schrijft, vertaalt de compiler deze in precies dezelfde waarde . In één geval bedoelde u dat het (tot één cijfer) is afgekapt als 0.3
, terwijl u in het andere geval het bedoelde te zijn afgekapt als 0.2
, maar Python kan alleen geef een antwoord. Dit is een fundamentele beperking van Python, of inderdaad elke programmeertaal zonder luie evaluatie. De truncatiefunctie heeft alleen toegang tot de binaire waarde die is opgeslagen in het geheugen van de computer, niet de reeks die u daadwerkelijk in de broncode hebt getypt. 1 sup>
Als u de volgorde van bits terug in een decimaal getal decoderen, met behulp van het IEEE 64-bits drijvend-puntformaat, krijgt u
0.2999999999999999888977697537484345957637...
dus een naïeve implementatie zou 0.2
opleveren, ook al is dat waarschijnlijk niet wat je wilt. Voor meer informatie over drijvende-komma-representatiefouten, zie de Python-tutorial.
Het komt zelden voor dat u werkt met een drijvende-kommawaarde die zo dicht bij een rond getal ligt en toch opzettelijkniet gelijk is aan dat ronde getal. Dus bij het afkappen is het waarschijnlijk logisch om de “mooiste” decimale weergave te kiezen uit alles dat zou kunnen overeenkomen met de waarde in het geheugen. Python 2.7 en hoger (maar niet 3.0) bevat een verfijnd algoritme om precies dat te doen, waartoe we toegang hebben de standaard bewerking voor het opmaken van tekenreeksen.
'{}'.format(f)
Het enige voorbehoud is dat dit werkt als een g
formaatspecificatie, in die zin dat het exponentiële notatie gebruikt (1.23e+4
) als het getal groot of klein genoeg. Dus de methode moet deze zaak opvangen en er anders mee omgaan. Er zijn een paar gevallen waarin het gebruik van een f
formaatspecificatie in plaats daarvan een probleem veroorzaakt, zoals proberen om 3e-10
af te kappen tot 28 cijfers van precisie (het levert 0.0000000002999999999999999980
), en ik weet nog niet hoe ik daar het beste mee om kan gaan.
Als u daadwerkelijk werktmet float
s die heel dicht bij ronde getallen liggen maar opzettelijk niet gelijk zijn aan deze getallen (zoals 0.299999999999999998 of 99.959999999999994), zal dit een aantal valse positieven, dwz het rondt getallen af die je niet wilde afronden. In dat geval is de oplossing om een vaste precisie op te geven.
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
Het aantal nauwkeurige cijfers dat hier wordt gebruikt, doet er niet echt toe, het moet alleen groot genoeg zijn om ervoor te zorgen dat eventuele afrondingen die in de tekenreeksconversie worden uitgevoerd, de waarde niet “opdrijven” tot zijn mooie decimale weergave. Ik denk dat sys.float_info.dig + n + 2
in alle gevallen voldoende kan zijn, maar anders moet 2
misschien worden verhoogd, en het kan geen kwaad om doe dit.
In eerdere versies van Python (tot 2.6 of 3.0) was de opmaak van getallen met drijvende komma veel grover, en leverde regelmatig dingen op als
>>> 1.1
1.1000000000000001
Als dit uw situatie is, als u wel“mooie” decimale representaties wilt gebruiken voor het afkappen, kunt u (voor zover ik weet) alleen een aantal cijfers kiezen, minder dan de volledige precisie weergegeven door een float
, en rond het getal af op zoveel cijfers voordat het wordt afgekapt. Een typische keuze is 12,
'%.12f' % f
maar je kunt dit aanpassen aan de nummers die je gebruikt.
1Nou… ik heb gelogen. Technisch gezien kun je Python kuntde opdracht geven om zijn eigen broncode opnieuw te ontleden en het deel te extraheren dat overeenkomt met het eerste argument dat je doorgeeft aan de afkapfunctie. Als dat argument een letterlijke drijvende komma is, kun je het gewoon een bepaald aantal plaatsen achter de komma afsnijden en dat teruggeven. Deze strategie werkt echter niet als het argument een variabele is, wat het vrij nutteloos maakt. Het volgende wordt alleen gepresenteerd voor amusementswaarde:
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
Dit veralgemenen om het geval aan te pakken waarin je een variabele doorgeeft, lijkt een verloren zaak, omdat je achteruit door de uitvoering van het programma zou moeten gaan totdat je de letterlijke drijvende komma vindt die de variabele zijn waarde gaf. Als er al een is. De meeste variabelen worden geïnitialiseerd op basis van gebruikersinvoer of wiskundige uitdrukkingen, in welk geval de binaire weergave alles is.
Antwoord 2, autoriteit 90%
round(1.923328437452, 3)
Zie Python’s documentatie over de standaardtypen. Je moet een beetje naar beneden scrollen om bij de ronde-functie te komen. In wezen geeft het tweede getal aan op hoeveel decimalen het moet worden afgerond.
Antwoord 3, autoriteit 27%
Het resultaat van round
is een float, dus pas op (voorbeeld is van Python 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
Je bent beter af als je een opgemaakte tekenreeks gebruikt:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
Antwoord 4, autoriteit 18%
n = 1.923328437452
str(n)[:4]
Antwoord 5, autoriteit 11%
Op mijn Python 2.7-prompt:
>>> int(1.923328437452 * 1000)/1000.0
1.923
Antwoord 6, autoriteit 9%
Eenvoudig python-script –
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
Antwoord 7, autoriteit 9%
De echt pythonische manier om het te doen is
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
of korter:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
Bijwerken
Meestal zit het probleem niet in het afkappen van floats zelf, maar in het oneigenlijke gebruik van float-nummers vóórafronding.
Bijvoorbeeld: int(0.7*3*100)/100 == 2.09
.
Als u gedwongenbent om floats te gebruiken (u versnelt uw code bijvoorbeeld met numba
), is het beter om centen te gebruiken als “interne weergave” van prijzen: (70*3 == 210
) en vermenigvuldig/deel de ingangen/uitgangen.
Antwoord 8, autoriteit 7%
def trunc(num, digits):
sp = str(num).split('.')
return '.'.join([sp[0], sp[1][:digits]])
Dit zou moeten werken. Het zou u de inkorting moeten geven waarnaar u op zoek bent.
Antwoord 9, autoriteit 6%
Zoveel van de antwoorden op deze vraag zijn gewoon helemaal fout. Ze ronden drijvers af (in plaats van afkappen) of werken niet in alle gevallen.
Dit is het beste Google-resultaat als ik zoek naar ‘Python truncate float’, een concept dat heel eenvoudig is en betere antwoorden verdient. Ik ben het met Hatchkins eens dat het gebruik van de module decimal
de pythonische manier is om dit te doen, dus ik geef hier een functie die volgens mij de vraag correct beantwoordt en die in alle gevallen werkt zoals verwacht.
Als een kanttekening kunnen fractionele waarden over het algemeen niet exact worden weergegeven door binaire variabelen met drijvende komma (zie hiervoor een bespreking hiervan), daarom retourneert mijn functie een string.
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()
Antwoord 10, Autoriteit 3%
Ik heb zoiets gedaan:
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
Antwoord 11, Autoriteit 3%
Als u zin in een wiskundig bent, werkt dit voor + ve-nummers:
>>> v = 1.923328437452
>>> v - v % 1e-3
1.923
Antwoord 12, Autoriteit 3%
>>> floor((1.23658945) * 10**4) / 10**4
1.2365
# delen en vermenigvuldig met 10 ** Aantal gewenste cijfers
Antwoord 13, Autoriteit 2%
u kunt doen:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
Testen:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
Antwoord 14, autoriteit 2%
Ik wilde even vermelden dat de oude “maak round() with floor()”-truc van
round(f) = floor(f+0.5)
kan worden omgedraaid om floor() van round() te maken
floor(f) = round(f-0.5)
Hoewel beide regels breken rond negatieve getallen, is het gebruik ervan niet ideaal:
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))
Antwoord 15, autoriteit 2%
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
Antwoord 16, autoriteit 2%
Korte en gemakkelijke variant
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
Antwoord 17, autoriteit 2%
Bij het gebruik van een panda-df werkte dit voor mij
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)
Antwoord 18
int(16.5);
dit geeft een geheel getal van 16, d.w.z. trunc, kan geen decimalen specificeren, maar denk dat je dat kunt doen door
import math;
def trunc(invalue, digits):
return int(invalue*math.pow(10,digits))/math.pow(10,digits);
Antwoord 19
Hier is een gemakkelijke manier:
def truncate(num, res=3):
return (floor(num*pow(10, res)+0.5))/pow(10, res)
voor num = 1.923328437452, dit geeft 1.923
Antwoord 20
def trunc(f,n):
return ('%.16f' % f)[:(n-16)]
Antwoord 21
Een algemene en eenvoudige functie om te gebruiken:
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
Antwoord 22
Er is een eenvoudige oplossing in python 3. Waar te knippen heb ik gedefinieerd met een hulpvariabele decPlace om het gemakkelijk aan te passen.
f = 1.12345
decPlace= 4
f_cut = int(f * 10**decPlace) /10**decPlace
Uitvoer:
f = 1.1234
Hopelijk helpt het.
Antwoord 23
De meeste antwoorden zijn naar mijn mening veel te ingewikkeld, wat dacht je hiervan?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Gewoon scannen naar de index van ‘.’ en naar wens afkappen (geen afronding).
Converteer string naar float als laatste stap.
Of in jouw geval als je een float als invoer krijgt en een string als uitvoer wilt:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
Antwoord 24
gebruik numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Antwoord 25
Iets dat eenvoudig genoeg is om in een lijst te passen, zonder bibliotheken of andere externe afhankelijkheden. Voor Python >=3.6 is het heel eenvoudig om met f-strings te schrijven.
Het idee is om de string-conversie de afronding op één plaats meer te laten doen dan je nodig hebten dan het laatste cijfer af te hakken.
>>> 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']
Natuurlijk vindt hier afrondingplaats (namelijk voor het vierde cijfer), maar afronding op een gegeven momentis onvermijdelijk. Als de overgang tussen truncatie en afronding relevant is, is hier een iets beter voorbeeld:
>>> 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: nullen aan de rechterkant verwijderen
>>> 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']
Antwoord 26
Het kernidee dat hiergegeven wordt, lijkt mij de beste aanpak voor dit probleem.
Helaas heeft het minder stemmen gekregen, terwijl het later antwoordmet meer stemmen niet compleet is (zoals te zien is in de opmerkingen). Hopelijk biedt de onderstaande implementatie een korte encomplete oplossing voor truncatie.
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return l[0] + '.' + l[1][:digits]
die moet zorgen voor alle hoekgevallen die hieren hier.
Antwoord 27
Ik denk dat een betere versie zou zijn om de index
van Decimal Point .
en vervolgens om de snaarplak dienovereenkomstig te nemen:
def truncate(number, n_digits:int=1)->float:
'''
:param number: real number ℝ
:param n_digits: Maximum number of digits after the decimal point after truncation
:return: truncated floating point number with at least one digit after decimal point
'''
decimalIndex = str(number).find('.')
if decimalIndex == -1:
return float(number)
else:
return float(str(number)[:decimalIndex+n_digits+1])
Antwoord 28
Ben ook een python newbie en na het maken van wat bits en stukken hier, bied ik mijn twee cent
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str (int (time.time ())) Neemt de tijd epoch als int en converteer het naar string en doe mee met …
str (DateTime.now (). Microseconde) [: 3] die de microseconden alleen retourneert, converteren naar string en afknat naar de eerste 3 tekens
Antwoord 29
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n
ANTWOORD 30
Als u bedoelt bij het afdrukken, moet het volgende werken:
print '%.3f' % number