Chcę usunąć cyfry z pływaka, aby mieć stałą liczbę cyfr po kropce, na przykład:
1.923328437452 -> 1.923
Muszę wyprowadzić jako ciąg do innej funkcji, a nie drukować.
Chcę też zignorować utracone cyfry, a nie je zaokrąglić.
Chcę usunąć cyfry z pływaka, aby mieć stałą liczbę cyfr po kropce, na przykład:
1.923328437452 -> 1.923
Muszę wyprowadzić jako ciąg do innej funkcji, a nie drukować.
Chcę też zignorować utracone cyfry, a nie je zaokrąglić.
Odpowiedzi:
Po pierwsze, funkcja dla tych, którzy chcą po prostu kopiować i wklejać kod:
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]])
Dotyczy to Pythona 2.7 i 3.1+. W przypadku starszych wersji nie można uzyskać tego samego efektu „inteligentnego zaokrąglania” (przynajmniej nie bez mnóstwa skomplikowanego kodu), ale zaokrąglanie do 12 miejsc po przecinku przed obcięciem będzie działać przez większość czasu:
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]])
Podstawową metodą jest przekonwertowanie wartości na ciąg z pełną precyzją, a następnie odcięcie wszystkiego poza wymaganą liczbę znaków. Drugi krok jest łatwy; można to zrobić za pomocą manipulacji na łańcuchach
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
lub decimal
moduł
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
Pierwszy krok, konwersja na łańcuch, jest dość trudny, ponieważ istnieje kilka par literałów zmiennoprzecinkowych (tj. Tego, co piszesz w kodzie źródłowym), które tworzą tę samą reprezentację binarną, a mimo to powinny być obcięte w inny sposób. Na przykład rozważmy 0,3 i 0,29999999999999998. Jeśli piszesz 0.3
w programie w języku Python, kompilator koduje go przy użyciu formatu zmiennoprzecinkowego IEEE do sekwencji bitów (zakładając 64-bitową liczbę zmiennoprzecinkową)
0011111111010011001100110011001100110011001100110011001100110011
Jest to wartość najbliższa 0,3, którą można dokładnie przedstawić jako wartość zmiennoprzecinkową IEEE. Ale jeśli piszesz 0.29999999999999998
w programie w Pythonie, kompilator tłumaczy to na dokładnie tę samą wartość . W jednym przypadku miałeś na myśli obcięcie go (do jednej cyfry) jako 0.3
, podczas gdy w innym przypadku chciałeś, aby został skrócony jako 0.2
, ale Python może udzielić tylko jednej odpowiedzi. Jest to podstawowe ograniczenie Pythona, a właściwie każdego języka programowania bez leniwej oceny. Funkcja obcinania ma dostęp tylko do wartości binarnej przechowywanej w pamięci komputera, a nie do ciągu, który faktycznie wpisałeś w kodzie źródłowym. 1
Jeśli zdekodujesz sekwencję bitów z powrotem do liczby dziesiętnej, ponownie używając 64-bitowego formatu zmiennoprzecinkowego IEEE, otrzymasz
0.2999999999999999888977697537484345957637...
więc wpadłaby naiwna implementacja, 0.2
nawet jeśli prawdopodobnie nie tego chcesz. Aby uzyskać więcej informacji na temat błędu reprezentacji zmiennoprzecinkowej, zobacz samouczek języka Python .
Bardzo rzadko można pracować z wartością zmiennoprzecinkową, która jest tak bliska okrągłej liczbie, ale celowo nie jest równa tej okrągłej liczbie. Dlatego podczas obcinania prawdopodobnie sensowne jest wybranie „najładniejszej” reprezentacji dziesiętnej spośród wszystkich, które mogą odpowiadać wartości w pamięci. Python 2.7 i nowsze (ale nie 3.0) zawierają wyrafinowany algorytm, który to robi , do którego możemy uzyskać dostęp poprzez domyślną operację formatowania ciągu.
'{}'.format(f)
Jedynym zastrzeżeniem jest to, że działa to jak g
specyfikacja formatu, w tym sensie, że używa notacji wykładniczej ( 1.23e+4
), jeśli liczba jest wystarczająco duża lub wystarczająco mała. Więc metoda musi wychwycić ten przypadek i obsłużyć go inaczej. Jest kilka przypadków, w których użycie f
specyfikacji formatu zamiast tego powoduje problem, na przykład próba skrócenia 3e-10
dokładności do 28 cyfr (daje to 0.0000000002999999999999999980
), i nie jestem jeszcze pewien, jak najlepiej sobie z nimi poradzić.
Jeśli rzeczywiście są pracy z float
S, które są bardzo zbliżone do okrągłych liczb, ale celowo nie równa się do nich (jak 0.29999999999999998 lub 99.959999999999994), to będzie produkować kilka fałszywych alarmów, to znaczy będziemy okrągłych liczb, które nie chcą zaokrąglone. W takim przypadku rozwiązaniem jest określenie stałej precyzji.
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
Liczba precyzyjnych cyfr, których należy tu użyć, nie ma tak naprawdę znaczenia, musi być tylko wystarczająco duża, aby zapewnić, że jakiekolwiek zaokrąglenia wykonywane podczas konwersji ciągów nie „podbijają” wartości do jej ładnej reprezentacji dziesiętnej. Myślę, że sys.float_info.dig + n + 2
może wystarczyć we wszystkich przypadkach, ale jeśli nie, 2
może to wymagać zwiększenia i nie zaszkodzi to zrobić.
We wcześniejszych wersjach Pythona (do 2.6 lub 3.0) formatowanie liczb zmiennoprzecinkowych było dużo bardziej prymitywne i regularnie powodowało takie rzeczy, jak
>>> 1.1
1.1000000000000001
Jeśli to jest Twoja sytuacja, jeśli nie chcesz korzystać z „nice” reprezentacje dziesiętne do obcinania, wszystko można zrobić (o ile wiem), to wybrać pewną liczbę cyfr, mniej niż pełnej precyzji reprezentowana przez float
i Round numer do tylu cyfr przed skróceniem go. Typowy wybór to 12,
'%.12f' % f
ale możesz to dostosować do używanych liczb.
1 Cóż ... skłamałem. Technicznie, można polecić Python, aby ponownie przeanalizować swój własny kod źródłowy i wyodrębnić część odpowiadającą pierwszego argumentu możesz przekazać do funkcji przycinania. Jeśli ten argument jest literałem zmiennoprzecinkowym, możesz po prostu odciąć go o określoną liczbę miejsc po przecinku i zwrócić to. Jednak ta strategia nie działa, jeśli argument jest zmienną, co czyni ją dość bezużyteczną. Poniższe informacje przedstawiono wyłącznie w celach rozrywkowych:
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
Uogólnienie tego, aby obsłużyć przypadek, w którym przekazujesz zmienną, wydaje się być utraconą przyczyną, ponieważ musiałbyś prześledzić wstecz przez wykonanie programu, aż znajdziesz literał zmiennoprzecinkowy, który nadał zmiennej jej wartość. Jeśli w ogóle istnieje. Większość zmiennych zostanie zainicjowana na podstawie danych wejściowych użytkownika lub wyrażeń matematycznych, w takim przypadku wystarczy reprezentacja binarna.
applymap()
.). Może jest sposób, aby cała operacja była bardziej wydajna, ale to byłaby kwestia osobnego pytania.
round(1.923328437452, 3)
Zobacz dokumentację Pythona na temat standardowych typów . Musisz trochę przewinąć w dół, aby przejść do funkcji rundy. Zasadniczo druga liczba mówi, do ilu miejsc po przecinku należy ją zaokrąglić.
Wynik round
jest float, więc uważaj (przykład pochodzi z Pythona 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
Lepiej będzie, jeśli użyjesz sformatowanego ciągu:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
round(1.23456, 3)
jest 1.235
i nie jest1.2350000000000001
n = 1.923328437452
str(n)[:4]
2
, będziesz mieć kropkę dziesiętną .
na końcu ciągu - myślę, że nie jest to dobre rozwiązanie.
Prosty skrypt w Pythonie -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
Prawdziwie pytoniczny sposób to zrobić
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
lub krócej:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
Aktualizacja
Zwykle problemem nie jest samo obcinanie liczb zmiennoprzecinkowych, ale niewłaściwe użycie liczb zmiennoprzecinkowych przed zaokrągleniem.
Na przykład: int(0.7*3*100)/100 == 2.09
.
Jeśli jesteś zmuszony używać pływaków (powiedzmy, że przyspieszasz swój kod numba
), lepiej użyć centów jako „wewnętrznej reprezentacji” cen: ( 70*3 == 210
) i pomnożyć / podzielić dane wejściowe / wyjściowe.
int(0.7*3*100)/100 == 2.09
. Gdzie poszedł mój 1 cent?
ImportError: cannot import name 'D'
, wierzę, że chciał zrobić o nazwie import nie?
Tak wiele odpowiedzi udzielonych na to pytanie jest po prostu całkowicie błędnych. Albo zaokrąglają liczby zmiennoprzecinkowe w górę (zamiast skracać), albo nie działają we wszystkich przypadkach.
To jest najlepszy wynik Google, kiedy wyszukuję „Python truncate float”, koncepcja, która jest naprawdę prosta i zasługuje na lepsze odpowiedzi. Zgadzam się z Hatchkinsem, że użycie decimal
modułu jest pythonowym sposobem na zrobienie tego, więc podaję tutaj funkcję, która moim zdaniem poprawnie odpowiada na pytanie i która działa zgodnie z oczekiwaniami we wszystkich przypadkach.
Na marginesie, wartości ułamkowe na ogół nie mogą być dokładnie reprezentowane przez binarne zmienne zmiennoprzecinkowe (zobacz tutaj omówienie tego), dlatego moja funkcja zwraca ciąg.
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()
Zrobiłem coś takiego:
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
Możesz to zrobić:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
testowanie:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
Podczas używania pandy df to działało dla mnie
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)
Chciałem tylko wspomnieć, że stara sztuczka "make round () z floor ()"
round(f) = floor(f+0.5)
można odwrócić, aby podłoga () z rundy ()
floor(f) = round(f-0.5)
Chociaż obie te zasady łamią liczby ujemne, użycie ich jest mniej niż idealne:
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 trunc(f,n):
return ('%.16f' % f)[:(n-16)]
Ogólna i prosta funkcja w użyciu:
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 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
Moim zdaniem większość odpowiedzi jest zbyt skomplikowana, a co powiesz na to?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Po prostu wyszukuję indeks „.” i obcinaj według potrzeb (bez zaokrąglania). Konwertuj ciąg na zmiennoprzecinkowy jako ostatni krok.
Lub w twoim przypadku, jeśli otrzymujesz zmiennoprzecinkowy jako dane wejściowe i chcesz, aby łańcuch jako wyjście:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
użyj numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Coś na tyle prostego, aby zmieściło się w liście, bez bibliotek ani innych zewnętrznych zależności. W przypadku Pythona> = 3.6 bardzo łatwo jest pisać za pomocą f-strings.
Chodzi o to, aby konwersja ciągów zaokrągliła do jednego miejsca więcej niż potrzebujesz, a następnie odcięła ostatnią cyfrę.
>>> 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']
Oczywiście zachodzi tutaj zaokrąglanie (mianowicie dla czwartej cyfry), ale zaokrąglenia w pewnym momencie nie da się uniknąć. Jeśli przejście między obcinaniem a zaokrąglaniem jest istotne, oto nieco lepszy przykład:
>>> 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: usunięcie zer po prawej stronie
>>> 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']
Podana tutaj podstawowa idea wydaje mi się najlepszym podejściem do tego problemu. Niestety, otrzymała mniej głosów, a późniejsza odpowiedź, która ma więcej głosów, nie jest kompletna (jak zauważono w komentarzach). Miejmy nadzieję, że poniższa implementacja zapewnia krótkie i kompletne rozwiązanie do obcięcia .
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return (l[0]+'.'+l[1][:digits])
który powinien zająć się wszystkimi narożnymi skrzynkami znalezionymi tutaj i tutaj .
Jestem również nowicjuszem w Pythonie i po skorzystaniu z kilku kawałków tutaj, oferuję moje dwa centy
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str (int (time.time ())) weźmie epokę czasu jako int i skonwertuje ją na łańcuch i połączy z ... str (datetime.now (). microsecond) [: 3], która zwraca tylko mikrosekundy, konwertuje ciągnąć i obcinać do pierwszych 3 znaków
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n