Python - abs vs fabs

107

Zauważyłem, że w Pythonie istnieją dwie podobnie wyglądające metody znajdowania wartości bezwzględnej liczby:

Pierwszy

abs(-5)

druga

import math
math.fabs(-5)

Czym różnią się te metody?

Mateusz Jagiełło
źródło

Odpowiedzi:

127

math.fabs()konwertuje swój argument na float, jeśli może (jeśli nie może, zgłasza wyjątek). Następnie przyjmuje wartość bezwzględną i zwraca wynik w postaci liczby zmiennoprzecinkowej.

Oprócz abs()liczb zmiennoprzecinkowych działa również z liczbami całkowitymi i liczbami zespolonymi. Jego typ zwracany zależy od typu argumentu.

In [7]: type(abs(-2))
Out[7]: int

In [8]: type(abs(-2.0))
Out[8]: float

In [9]: type(abs(3+4j))
Out[9]: float

In [10]: type(math.fabs(-2))
Out[10]: float

In [11]: type(math.fabs(-2.0))
Out[11]: float

In [12]: type(math.fabs(3+4j))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/home/npe/<ipython-input-12-8368761369da> in <module>()
----> 1 type(math.fabs(3+4j))

TypeError: can't convert complex to float
NPE
źródło
4
absdziała z czymś więcej niż tylko liczbami całkowitymi i zmiennoprzecinkowymi, a typ wyniku nie zawsze jest taki sam jak argument, np abs(3+4j).
agf
1
dodaj komentarz o tym, że fabstrwa to dłużej ze względu na jego zawsze pływający charakter i masz właściwą odpowiedź!
Patrick Perini
@agf: Dziękuję za przypomnienie mi o liczbach zespolonych. Poza tym, do jakich innych klas rzeczy można z __builtin__.abs()powodzeniem zastosować?
NPE
5
@aix Dowolna klasa zdefiniowana przez użytkownika, która definiuje __abs__magiczną metodę
agf
9

Edycja: jak zasugerował @aix, lepszy (bardziej sprawiedliwy) sposób porównania różnicy prędkości:

In [1]: %timeit abs(5)
10000000 loops, best of 3: 86.5 ns per loop

In [2]: from math import fabs

In [3]: %timeit fabs(5)
10000000 loops, best of 3: 115 ns per loop

In [4]: %timeit abs(-5)
10000000 loops, best of 3: 88.3 ns per loop

In [5]: %timeit fabs(-5)
10000000 loops, best of 3: 114 ns per loop

In [6]: %timeit abs(5.0)
10000000 loops, best of 3: 92.5 ns per loop

In [7]: %timeit fabs(5.0)
10000000 loops, best of 3: 93.2 ns per loop

In [8]: %timeit abs(-5.0)
10000000 loops, best of 3: 91.8 ns per loop

In [9]: %timeit fabs(-5.0)
10000000 loops, best of 3: 91 ns per loop

Więc wydaje się, że abs()ma tylko niewielką przewagę szybkości nad fabs()liczbami całkowitymi. Dla pływaków abs()i fabs()zademonstruj podobną prędkość.


Oprócz tego, co powiedział @aix, jeszcze jedna rzecz do rozważenia to różnica prędkości:

In [1]: %timeit abs(-5)
10000000 loops, best of 3: 102 ns per loop

In [2]: import math

In [3]: %timeit math.fabs(-5)
10000000 loops, best of 3: 194 ns per loop

Więc abs()jest szybszy niż math.fabs().

KZ
źródło
3
Nie porównujesz tam jabłek do jabłek. Użyj from math import fabsna pewno i spróbuj -5.0dla obu.
agf
Również niewiarygodne wyniki czasowe? Najpierw miałeś abs (-5) przy 102ns, a później pokazałeś to jako 88,3ns. Nigdy nie wyciągaj wniosków z jednej serii jakiegokolwiek testu porównawczego, nawet jeśli wewnętrznie próbuje uniknąć problemów w miarę upływu czasu.
Peter Hansen,
1
Jeszcze dwa punkty: to nadal porównuje jabłka i pomarańcze, ponieważ abs jest wyszukiwany w poleceniach wbudowanych, podczas gdy fabs znajduje się w przestrzeni nazw modułu. Wykonaj „xabs = abs”, a następnie xabs (num), aby usunąć ten efekt. Ponadto w Pythonie 3.2 rzeczy się trochę zmieniają i najwyraźniej abs () jest nieco szybszy (> 2x), przynajmniej na pływakach.
Peter Hansen,
1
@PeterHansen Masz rację, te pochodzą z dwóch przebiegów pod różnymi obciążeniami systemu. Chociaż porównując w ramach tego samego zestawu testów, względna różnica jest nadal aktualna. Dzięki za wskazanie różnicy w przestrzeni nazw - nie brałem tego pod uwagę. Nie próbowałem tego na 3.2, ale dobrze wiedzieć! Nieco później zaktualizuję swoją odpowiedź Waszymi sugestiami :) Jeszcze raz dziękuję!
KZ
3

math.fabs()zawsze zwraca abs()liczbę zmiennoprzecinkową, podczas gdy może zwracać liczbę całkowitą.

Tadeck
źródło
6
absmoże zwrócić dowolny typ, w zależności od __abs__specjalnej metody typu, do którego jest wywoływana.
agf
0

abs(): Zwraca wartość bezwzględną zgodnie z argumentem, tj. Jeśli argument jest typu int, to zwraca int, jeśli argument jest zmiennoprzecinkowy, zwraca wartość typu float. Działa również na zmiennej złożonej, tj. abs(a+bj)Również działa i zwraca wartość bezwzględną, tjmath.sqrt(((a)**2)+((b)**2)

math.fabs(): Działa tylko na wartościach całkowitych lub zmiennoprzecinkowych. Zawsze zwraca bezwzględną wartość zmiennoprzecinkową bez względu na typ argumentu (z wyjątkiem liczb zespolonych).

Rahul Talole
źródło