Hoe float-waarden afkappen?

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 decimalmodule

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.3in 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.29999999999999998in 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

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.2opleveren, 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 gformaatspecificatie, 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 fformaatspecificatie in plaats daarvan een probleem veroorzaakt, zoals proberen om 3e-10af 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 floats 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 + 2in alle gevallen voldoende kan zijn, maar anders moet 2misschien 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 roundis 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 decimalde 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 indexvan 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

Other episodes