Jaki jest najlepszy sposób porównania pływaków dla prawie równości w Pythonie?

331

Powszechnie wiadomo, że porównywanie liczb zmiennoprzecinkowych w celu zapewnienia równości jest trochę kłopotliwe z powodu problemów z zaokrąglaniem i precyzją.

Na przykład: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

Jaki jest zalecany sposób radzenia sobie z tym w Pythonie?

Na pewno jest gdzieś standardowa funkcja biblioteki?

Gordon Wrigley
źródło
@tolomea: Skoro zależy to od aplikacji, danych i domeny problemu - i to tylko jeden wiersz kodu - dlaczego miałaby istnieć „standardowa funkcja biblioteki”?
S.Lott,
9
@ S.Lott: all, any, max, minto każdy w zasadzie jednej wkładki, i nie są one przewidziane tylko w bibliotece, są wbudowane funkcje. Powody BDFL nie są takie. Jedna linia kodu, którą większość ludzi pisze, jest dość niewyszukana i często nie działa, co jest silnym powodem, aby zapewnić coś lepszego. Oczywiście każdy moduł zapewniający inne strategie musiałby również zawierać zastrzeżenia opisujące, kiedy są odpowiednie, a co ważniejsze, gdy nie są. Analiza numeryczna jest trudna, nie jest wielką hańbą, że projektanci języków zwykle nie próbują narzędzi, które mogłyby w tym pomóc.
Steve Jessop,
@Steve Jessop. Te funkcje zorientowane na kolekcję nie mają zależności aplikacji, danych i problemowych domen, które mają zmiennoprzecinkowe. Tak więc „jedna linijka” wyraźnie nie jest tak ważna, jak prawdziwe powody. Analiza numeryczna jest trudna i nie może być pierwszorzędną częścią biblioteki języków ogólnego przeznaczenia.
S.Lott,
6
@ S.Lott: Prawdopodobnie zgodziłbym się, gdyby standardowa dystrybucja Pythona nie zawierała wielu modułów dla interfejsów XML. Oczywiście fakt, że różne aplikacje muszą robić coś inaczej, wcale nie jest przeszkodą w umieszczaniu modułów w zestawie podstawowym, aby robić to w ten czy inny sposób. Z pewnością istnieją sztuczki służące do porównywania pływaków, które są często ponownie używane, przy czym najbardziej podstawowym jest określona liczba ulps. Więc tylko częściowo się zgadzam - problem polega na tym, że analiza numeryczna jest trudna. Python może co do zasady dostarczać narzędzia, które w jakiś sposób mogą to ułatwić. Chyba nikt nie zgłosił się na ochotnika.
Steve Jessop,
4
Ponadto, „sprowadza się do jednej trudnej do zaprojektowania linii kodu” - jeśli nadal jest to jednowierszowa, gdy robisz to poprawnie, myślę, że twój monitor jest szerszy niż mój ;-). W każdym razie uważam, że cały obszar jest dość wyspecjalizowany, w tym sensie, że większość programistów (w tym ja) bardzo rzadko go używa. W połączeniu z trudnością, nie znajdzie się na szczycie listy „najbardziej poszukiwanych” bibliotek podstawowych w większości języków.
Steve Jessop

Odpowiedzi:

324

Python 3.5 dodaje funkcje math.iscloseicmath.isclose zgodnie z opisem w PEP 485 .

Jeśli używasz wcześniejszej wersji Pythona, równoważna funkcja jest podana w dokumentacji .

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

rel_toljest względną tolerancją, pomnożoną przez większą z wielkości dwóch argumentów; wraz ze wzrostem wartości rośnie dozwolona różnica między nimi, jednocześnie uznając je za równe.

abs_toljest bezwzględną tolerancją, którą stosuje się w obecnej postaci we wszystkich przypadkach. Jeśli różnica jest mniejsza niż jedna z tych tolerancji, wartości są uważane za równe.

Mark Ransom
źródło
26
Uwaga, kiedy alub bjest a numpy array, numpy.isclosedziała.
dbliss
6
@marsh rel_toljest względną tolerancją , pomnożoną przez większą z wielkości dwóch argumentów; wraz ze wzrostem wartości rośnie dozwolona różnica między nimi, jednocześnie uznając je za równe. abs_toljest bezwzględną tolerancją, którą stosuje się w obecnej postaci we wszystkich przypadkach. Jeśli różnica jest mniejsza niż jedna z tych tolerancji, wartości są uważane za równe.
Mark Ransom,
5
Aby nie pomniejszyć wartości tej odpowiedzi (myślę, że jest dobra), warto zauważyć, że dokumentacja mówi również: „Sprawdzanie błędów modulo itp., Funkcja zwróci wynik ...” Innymi słowy, isclosefunkcja (powyżej) nie jest pełną implementacją.
rkersh
5
Przepraszamy za ożywienie starego wątku, ale warto zauważyć, że isclosezawsze spełnia ono mniej konserwatywne kryterium. Wspominam o tym tylko dlatego, że takie zachowanie jest dla mnie sprzeczne z intuicją. Gdybym określił dwa kryteria, zawsze oczekiwałbym, że mniejsza tolerancja zastąpi większą.
Mackie Messer
3
@MackieMesser masz oczywiście prawo do swojej opinii, ale to zachowanie miało dla mnie idealny sens. Z twojej definicji nic nigdy nie może być „bliskie” zeru, ponieważ względna tolerancja pomnożona przez zero jest zawsze równa zero.
Mark Ransom
71

Czy coś tak prostego jak poniższe nie jest wystarczająco dobre?

return abs(f1 - f2) <= allowed_error
Andrew White
źródło
8
Jak wskazuje link, który podałem, odejmowanie działa tylko wtedy, gdy znasz wcześniej przybliżoną wielkość liczb.
Gordon Wrigley,
8
Z mojego doświadczenia wynika, najlepszą metodą porównywania pływaków jest: abs(f1-f2) < tol*max(abs(f1),abs(f2)). Ten rodzaj tolerancji względnej jest jedynym sensownym sposobem na porównanie pływaków w ogóle, ponieważ zwykle występuje w nich błąd zaokrągleń w małych miejscach dziesiętnych.
Sesquipedal
2
Wystarczy dodać prosty przykład, dlaczego może nie działać >>> abs(0.04 - 0.03) <= 0.01:, daje False. UżywamPython 2.7.10 [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
schatten
3
@schatten, żeby być uczciwym, ten przykład ma więcej wspólnego z maszynową binarną precyzją / formatami niż z konkretnym algorytmem porównawczym. Po wprowadzeniu 0,03 do systemu, tak naprawdę nie jest to liczba, która dotarła do procesora.
Andrew White,
2
@AndrewWhite ten przykład pokazuje, że abs(f1 - f2) <= allowed_errornie działa zgodnie z oczekiwaniami.
schatten
45

Zgadzam się, że odpowiedź Garetha jest prawdopodobnie najbardziej odpowiednia jako lekka funkcja / rozwiązanie.

Pomyślałem jednak, że przydałoby się zauważyć, że jeśli używasz NumPy lub zastanawiasz się nad tym, istnieje do tego funkcja spakowana.

numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

Małe zastrzeżenie: instalacja NumPy może być łatwym doświadczeniem w zależności od platformy.

J.Makela
źródło
1
„Instalowanie Numpy może być łatwym doświadczeniem w zależności od platformy.” ... um Co? Na jakich platformach instalacja numpy jest „nietrywialna”? Co dokładnie uczyniło go nietrywialnym?
Jan
10
@John: trudno uzyskać 64-bitowy plik binarny dla systemu Windows. Trudno uzyskać numpy przez pipWindows.
Ben Bolker,
@Ternak: Tak, ale niektórzy z moich uczniów używają systemu Windows, więc muszę sobie z tym poradzić.
Ben Bolker
4
@BenBolker Jeśli musisz zainstalować otwartą platformę do nauki danych opartą na języku Python, najlepszym sposobem jest Anaconda continuum.io/downloads (pandy, numpy i więcej od razu po wyjęciu z pudełka)
jrovegno
Instalacja Anakondy jest banalna
endolith
13

Użyj decimalmodułu Pythona , który udostępnia Decimalklasę.

Z komentarzy:

Warto zauważyć, że jeśli wykonujesz ciężką pracę matematyczną i nie potrzebujesz absolutnie precyzji od dziesiętnej, może to naprawdę zapaść się w rzeczy. Spławiki są znacznie szybsze, ale nieprecyzyjne. Dziesiętne są niezwykle precyzyjne, ale powolne.

jathanizm
źródło
11

Nie znam niczego w standardowej bibliotece Pythona (ani gdzie indziej), która implementuje AlmostEqual2sComplementfunkcję Dawsona . Jeśli chcesz tego rodzaju zachowanie, musisz je wdrożyć samodzielnie. (W takim przypadku, zamiast używać sprytnych bitowych hacków Dawsona, prawdopodobnie lepiej byłoby użyć bardziej konwencjonalnych testów formularza if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2lub podobnego. Aby uzyskać zachowanie podobne do Dawsona, możesz powiedzieć coś if abs(a-b) <= eps*max(EPS,abs(a),abs(b))w stylu małej poprawki EPS; to nie jest dokładnie tak samo jak Dawson, ale ma podobny duch.

Gareth McCaughan
źródło
Nie do końca podążam za tym, co tu robisz, ale to ciekawe. Jaka jest różnica między eps, eps1, eps2 i EPS?
Gordon Wrigley,
eps1i eps2zdefiniuj względną i bezwzględną tolerancję: jesteś gotów pozwolić ai bróżnić się o eps1czasy, o ile są one większe eps2. epsjest pojedynczą tolerancją; jesteś przygotowany, aby pozwolić ai bróżnić się w przybliżeniu epsczasami, jak duże są, z zastrzeżeniem, że EPSzakłada się , że coś wielkości lub mniejszy jest wielkości EPS. Jeśli weźmiesz EPSpod uwagę najmniejszą niedenormalną wartość typu zmiennoprzecinkowego, jest to bardzo podobne do komparatora Dawsona (z wyjątkiem współczynnika 2 ^ # bitów, ponieważ Dawson mierzy tolerancję w ulps).
Gareth McCaughan,
2
Nawiasem mówiąc, zgadzam się z S. Lottem, że Właściwa Rzecz zawsze będzie zależeć od twojej rzeczywistej aplikacji, dlatego nie ma jednej standardowej funkcji bibliotecznej dla wszystkich twoich potrzeb porównania zmiennoprzecinkowego.
Gareth McCaughan
@ gareth-mccaughan W jaki sposób określa się „najmniejszą niedenormalną wartość typu zmiennoprzecinkowego” dla pythona?
Gordon Wrigley,
Ta strona docs.python.org/tutorial/floatingpoint.html mówi, że prawie wszystkie implementacje Pythona używają pływaków podwójnej precyzji IEEE-754, a strona en.wikipedia.org/wiki/IEEE_754-1985 mówi, że znormalizowane liczby najbliższe zeru wynoszą ± 2 * * −1022.
Gordon Wrigley,
11

Powszechna mądrość, że liczb zmiennoprzecinkowych nie można porównywać dla równości, jest niedokładna. Liczby zmiennoprzecinkowe nie różnią się od liczb całkowitych: Jeśli ocenisz „a == b”, otrzymasz prawdę, jeśli są to liczby identyczne, a fałsz w przeciwnym razie (przy założeniu, że dwa NaN nie są oczywiście liczbami identycznymi).

Rzeczywisty problem jest następujący: jeśli wykonałem kilka obliczeń i nie jestem pewien, czy dwie liczby, które muszę porównać, są dokładnie poprawne, to co? Ten problem jest taki sam dla liczb zmiennoprzecinkowych, jak dla liczb całkowitych. Jeśli ocenisz wyrażenie całkowite „7/3 * 3”, nie będzie ono porównywane z „7 * 3/3”.

Załóżmy więc, że zapytaliśmy „Jak porównać liczby całkowite w celu zapewnienia równości?” w takiej sytuacji. Nie ma jednej odpowiedzi; to, co powinieneś zrobić, zależy od konkretnej sytuacji, w szczególności od rodzaju błędów i tego, co chcesz osiągnąć.

Oto kilka możliwych opcji.

Jeśli chcesz uzyskać „prawdziwy” wynik, jeśli matematycznie dokładne liczby byłyby równe, możesz spróbować użyć właściwości obliczeń, które wykonujesz, aby udowodnić, że otrzymujesz te same błędy w tych dwóch liczbach. Jeśli jest to wykonalne, a porównasz dwie liczby, które wynikają z wyrażeń, które dałyby równe liczby, jeśli zostałyby dokładnie obliczone, uzyskasz „prawdziwość” z porównania. Inne podejście polega na tym, że możesz przeanalizować właściwości obliczeń i udowodnić, że błąd nigdy nie przekracza określonej kwoty, być może kwoty bezwzględnej lub kwoty odnoszącej się do jednego z danych wejściowych lub jednego z danych wyjściowych. W takim przypadku możesz zapytać, czy dwie obliczone liczby różnią się co najwyżej o tę kwotę, i zwrócić „prawda”, jeśli mieszczą się w przedziale. Jeśli nie możesz udowodnić błędu, możesz zgadywać i mieć nadzieję na najlepsze. Jednym ze sposobów zgadywania jest ocena wielu losowych próbek i sprawdzenie, jaki rozkład otrzymujesz w wynikach.

Oczywiście, ponieważ ustaliliśmy wymóg, abyś był „prawdziwy”, jeśli matematycznie dokładne wyniki są równe, pozostawiliśmy otwartą możliwość, abyś był „prawdziwy”, nawet jeśli są nierówne. (W rzeczywistości możemy spełnić ten wymóg, zawsze zwracając „prawda”. To sprawia, że ​​obliczenia są proste, ale generalnie niepożądane, dlatego omówię poprawę sytuacji poniżej.)

Jeśli chcesz uzyskać „fałszywy” wynik, jeśli matematyczne dokładne liczby byłyby nierówne, musisz udowodnić, że twoja ocena liczb daje różne liczby, jeśli matematyczne dokładne liczby byłyby nierówne. Może to być niemożliwe ze względów praktycznych w wielu typowych sytuacjach. Rozważmy więc alternatywę.

Przydatnym wymogiem może być uzyskanie „fałszywego” wyniku, jeśli matematyczne dokładne liczby różnią się o więcej niż pewną liczbę. Na przykład być może obliczymy, gdzie wędrowała piłka rzucona w grę komputerową, i chcemy wiedzieć, czy uderzyła w nietoperza. W tym przypadku z pewnością chcemy uzyskać „prawdziwą”, jeśli piłka uderzy w nietoperza, a także „fałszywą”, jeśli piłka jest daleko od nietoperza, i możemy zaakceptować niepoprawną „prawdziwą” odpowiedź, jeśli piłka matematycznie dokładna symulacja nie trafiła nietoperza, ale znajduje się w odległości milimetra od uderzenia nietoperza. W takim przypadku musimy udowodnić (lub zgadnąć / oszacować), że nasze obliczenia pozycji piłki i pozycji nietoperza mają łączny błąd co najwyżej jednego milimetra (dla wszystkich pozycji zainteresowania). To pozwoli nam zawsze wracać ”

To, jak zdecydujesz, co zwrócić, porównując liczby zmiennoprzecinkowe, zależy w dużej mierze od konkretnej sytuacji.

Jeśli chodzi o sprawdzanie granic błędów w obliczeniach, może to być skomplikowany temat. Każda implementacja zmiennoprzecinkowa wykorzystująca standard IEEE 754 w trybie zaokrąglania do najbliższego zwraca liczbę zmiennoprzecinkową najbliższą dokładnemu wynikowi dla dowolnej podstawowej operacji (w szczególności mnożenie, dzielenie, dodawanie, odejmowanie, pierwiastek kwadratowy). (W przypadku remisu, zaokrąglenia, tak więc niski bit jest parzysty.) (Zachowaj szczególną ostrożność przy pierwiastku kwadratowym i dzieleniu; twoja implementacja języka może używać metod, które nie są zgodne z IEEE 754). Z tego powodu wiemy, że błąd w pojedynczym wyniku wynosi najwyżej 1/2 wartości najmniej znaczącego bitu. (Gdyby było więcej, zaokrąglenie byłoby ustawione na inną liczbę, która mieści się w zakresie 1/2 wartości.)

Dalsza praca staje się znacznie bardziej skomplikowana; następnym krokiem jest wykonanie operacji, w której jedno z wejść ma już jakiś błąd. W przypadku prostych wyrażeń błędy te można śledzić przez obliczenia, aby osiągnąć granicę błędu końcowego. W praktyce odbywa się to tylko w kilku sytuacjach, takich jak praca nad wysokiej jakości biblioteką matematyczną. I oczywiście potrzebujesz dokładnej kontroli dokładnie nad tym, które operacje są wykonywane. Języki wysokiego poziomu często dają kompilatorowi dużo luzu, więc możesz nie wiedzieć, w jakiej kolejności są wykonywane operacje.

Jest o wiele więcej, co można napisać (i jest) na ten temat, ale muszę się na tym zatrzymać. Podsumowując, odpowiedź brzmi: nie ma procedury bibliotecznej dla tego porównania, ponieważ nie ma jednego rozwiązania, które byłoby w stanie zaspokoić większość potrzeb, które byłoby warte zastosowania w bibliotece. (Jeśli porównanie z przedziałem błędu względnego lub bezwzględnego wystarcza, możesz to zrobić po prostu bez procedury bibliotecznej).

Eric Postpischil
źródło
3
Z powyższej dyskusji z Garethem McCaughanem, prawidłowe porównanie z błędem względnym zasadniczo oznacza „abs (ab) <= eps max (2 * -1022, abs (a), abs (b))”, nie opisałbym tego tak proste i na pewno nie coś, co sam bym wypracował. Ponadto, jak zauważa Steve Jessop, ma on podobną złożoność jak max, min, dowolne i wszystkie, które są wbudowane. Zapewnienie porównania błędów względnych w standardowym module matematycznym wydaje się dobrym pomysłem.
Gordon Wrigley,
(7/3 * 3 == 7 * 3/3) ocenia True w pythonie.
xApple
@xApple: Właśnie uruchomiłem Python 2.7.2 na OS X 10.8.3 i wszedłem (7/3*3 == 7*3/3). Wydrukowano False.
Eric Postpischil,
3
Prawdopodobnie zapomniałeś wpisać from __future__ import division. Jeśli tego nie zrobisz, nie ma liczb zmiennoprzecinkowych, a porównanie dotyczy dwóch liczb całkowitych.
xApple
3
To ważna dyskusja, ale niezbyt pomocna.
Dan Hulme,
6

Jeśli chcesz używać go w kontekście testowania / TDD, powiedziałbym, że jest to standardowy sposób:

from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) #default is 7
Włodzimierz
źródło
5

math.isclose () został w tym celu dodany do Pythona 3.5 ( kod źródłowy ). Oto jego port dla Pythona 2. Różnica w stosunku do jednowierszowej Mark Ransom polega na tym, że może on poprawnie obsługiwać „inf” i „-inf”.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 implementation of Python 3.5 math.isclose()
    https://hg.python.org/cpython/file/tip/Modules/mathmodule.c#l1993
    '''
    # sanity check on the inputs
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError("tolerances must be non-negative")

    # short circuit exact equality -- needed to catch two infinities of
    # the same sign. And perhaps speeds things up a bit sometimes.
    if a == b:
        return True

    # This catches the case of two infinities of opposite sign, or
    # one infinity and one finite number. Two infinities of opposite
    # sign would otherwise have an infinite relative tolerance.
    # Two infinities of the same sign are caught by the equality check
    # above.
    if math.isinf(a) or math.isinf(b):
        return False

    # now do the regular computation
    # this is essentially the "weak" test from the Boost library
    diff = math.fabs(b - a)
    result = (((diff <= math.fabs(rel_tol * b)) or
               (diff <= math.fabs(rel_tol * a))) or
              (diff <= abs_tol))
    return result
użytkownik2745509
źródło
2

Pomocne okazało się następujące porównanie:

str(f1) == str(f2)
Kresimir
źródło
to jest interesujące, ale niezbyt praktyczne ze względu na str (.1 + .2) == .3
Gordon Wrigley
str (.1 + .2) == str (.3) zwraca True
Henrikh Kantuni
Jak to się różni od f1 == f2 - jeśli oba są bliskie, ale wciąż różnią się ze względu na precyzję, reprezentacje ciągów również będą nierówne.
MrMas
2
.1 + .2 == .3 zwraca False, a str (.1 + .2) == str (.3) zwraca True
Kresimir
4
W Python 3.7.2 str(.1 + .2) == str(.3)zwraca False. Metoda opisana powyżej działa tylko dla Pythona 2.
Danibix
1

W niektórych przypadkach, w których możesz wpłynąć na reprezentację numeru źródłowego, możesz przedstawić je jako ułamki zamiast liczb zmiennoprzecinkowych, używając liczb całkowitych i mianowników. W ten sposób możesz mieć dokładne porównania.

Aby uzyskać szczegółowe informacje, zobacz Moduł Frakcja z ułamków.

eis
źródło
1

Podobała mi się sugestia @Sesquipedal, ale z modyfikacją (specjalny przypadek użycia, gdy obie wartości wynoszą 0, zwraca False). W moim przypadku korzystałem z Pythona 2.7 i użyłem prostej funkcji:

if f1 ==0 and f2 == 0:
    return True
else:
    return abs(f1-f2) < tol*max(abs(f1),abs(f2))
IronYeti
źródło
1

Przydatne w przypadku, gdy chcesz się upewnić, że 2 liczby są takie same „do precyzji”, nie musisz określać tolerancji:

  • Znajdź minimalną precyzję 2 liczb

  • Zaokrąglij oba z nich do minimalnej precyzji i porównaj

def isclose(a,b):                                       
    astr=str(a)                                         
    aprec=len(astr.split('.')[1]) if '.' in astr else 0 
    bstr=str(b)                                         
    bprec=len(bstr.split('.')[1]) if '.' in bstr else 0 
    prec=min(aprec,bprec)                                      
    return round(a,prec)==round(b,prec)                               

Jak napisano, działa tylko dla liczb bez „e” w ich reprezentacji ciągu (co oznacza 0,9999999999995e-4 <liczba <= 0,9999999999995e11)

Przykład:

>>> isclose(10.0,10.049)
True
>>> isclose(10.0,10.05)
False
CptHwK
źródło
Nieograniczona koncepcja bliskości nie będzie ci dobrze służyć. isclose(1.0, 1.1)produkuje Falsei isclose(0.1, 0.000000000001)zwraca True.
kfsone
1

Aby porównać do podanego miejsca po przecinku bez atol/rtol:

def almost_equal(a, b, decimal=6):
    return '{0:.{1}f}'.format(a, decimal) == '{0:.{1}f}'.format(b, decimal)

print(almost_equal(0.0, 0.0001, decimal=5)) # False
print(almost_equal(0.0, 0.0001, decimal=4)) # True 
Vlad
źródło
1

To może być trochę brzydki hack, ale działa całkiem dobrze, gdy nie potrzebujesz więcej niż domyślna precyzja float (około 11 miejsc po przecinku).

Funkcja round_to używa metody formatowania z wbudowanej klasy str, aby zaokrąglić liczbę zmiennoprzecinkową do ciągu, który reprezentuje liczbę zmiennoprzecinkową z wymaganą liczbą miejsc po przecinku, a następnie stosuje wbudowaną funkcję eval do zaokrąglonego ciągu zmiennoprzecinkowego, aby wrócić na zmiennoprzecinkowy typ liczbowy.

Funkcja is_close stosuje prosty warunek do zaokrąglonej liczby zmiennoprzecinkowej.

def round_to(float_num, prec):
    return eval("'{:." + str(int(prec)) + "f}'.format(" + str(float_num) + ")")

def is_close(float_a, float_b, prec):
    if round_to(float_a, prec) == round_to(float_b, prec):
        return True
    return False

>>>a = 10.0
10.0
>>>b = 10.0001
10.0001
>>>print is_close(a, b, prec=3)
True
>>>print is_close(a, b, prec=4)
False

Aktualizacja:

Jak sugeruje @stepehjfox, czystszym sposobem na zbudowanie funkcji rount_to unikającej „eval” jest użycie zagnieżdżonego formatowania :

def round_to(float_num, prec):
    return '{:.{precision}f}'.format(float_num, precision=prec)

Zgodnie z tym samym pomysłem kod może być jeszcze prostszy przy użyciu świetnych nowych ciągów F (Python 3.6+):

def round_to(float_num, prec):
    return f'{float_num:.{prec}f}'

Moglibyśmy nawet wszystko to zawrzeć w jednej prostej i czystej funkcji „is_close” :

def is_close(a, b, prec):
    return f'{a:.{prec}f}' == f'{b:.{prec}f}'
Albert Alomar
źródło
1
Nie musisz używać, eval()aby uzyskać sparametryzowane formatowanie. Coś jak return '{:.{precision}f'.format(float_num, precision=decimal_precision) powinno to zrobić
stephenjfox
1
Źródło mojego komentarza i innych przykładów: pyformat.info/#param_align
stephenjfox
1
Dzięki @stephenjfox Nie wiedziałem o formatowaniu zagnieżdżonym. Przy okazji, w twoim przykładowym kodzie brakuje końcowych nawiasów klamrowych:return '{:.{precision}}f'.format(float_num, precision=decimal_precision)
Albert Alomar,
1
Dobry chwyt, a szczególnie dobrze wykonane ulepszenie strun. Wraz ze śmiercią Pythona 2 za rogiem, być może stanie się to normą
stephenjfox
0

Jeśli chodzi o błąd bezwzględny, możesz po prostu sprawdzić

if abs(a - b) <= error:
    print("Almost equal")

Niektóre informacje o tym, dlaczego float działa tak dziwnie w Pythonie https://youtu.be/v4HhvoNLILk?t=1129

Możesz także użyć math.isclose do względnych błędów

Rahul Sharma
źródło