Chcę funkcji, która zwraca -1 dla liczb ujemnych i +1 dla liczb dodatnich. http://en.wikipedia.org/wiki/Sign_function Łatwo jest napisać własny, ale wydaje się, że powinien być gdzieś w standardowej bibliotece.
Edycja: W szczególności szukałem funkcji działającej na liczbach zmiennoprzecinkowych.
x==0
. Zgodnie z IEEE 754 zero ujemne i zero dodatnie powinny być równe.Odpowiedzi:
Zaskoczony, nikt jeszcze nie opublikował bezpiecznej wersji C ++:
Korzyści:
copysign
jest powolny, szczególnie jeśli musisz się promować, a następnie ponownie zawęzić. Jest to bez rozgałęzień i doskonale optymalizujeOstrzeżenia:
< 0
Częścią kontroli wyzwala GCC-Wtype-limits
ostrzeżenie, gdy instancja dla typu unsigned. Można tego uniknąć, stosując pewne przeciążenia:(Co jest dobrym przykładem pierwszego zastrzeżenia).
źródło
std::copysign
wydaje się dla mnie doskonałym kodem: 4 instrukcje (wbudowane), brak rozgałęzień, całkowicie przy użyciu FPU. Przepis podany w tej odpowiedzi generuje natomiast znacznie gorszy kod (znacznie więcej instrukcji, w tym mnożenie, przechodzenie do przodu i do tyłu między jednostką całkowitą a jednostką FPU) ...copysign
int, promuje się on float / double i po powrocie musi się zawęzić. Twój kompilator może zoptymalizować tę promocję, ale nie mogę znaleźć niczego, co sugerowałoby standard. Aby zaimplementować signum za pomocą copysign, musisz ręcznie obsłużyć przypadek 0 - pamiętaj, aby uwzględnić to w każdym porównaniu wydajności.Nie znam standardowej funkcji do tego. Oto ciekawy sposób, aby to napisać:
Oto bardziej czytelny sposób:
Jeśli podoba Ci się operator trójskładnikowy, możesz to zrobić:
źródło
x==0
.<
,>
... da 1, jeśli określona relacja jest prawdziwa, i 0, jeśli jest fałszywa”0
„false”; każda inna wartość jest „prawdziwa”; jednak operatory relacji i równości zawsze zwracają0
lub1
(patrz Norma 6.5.8 i 6.5.9). - wartość wyrażeniaa * (x == 42)
jest albo0
czya
.copysign
całki,x
nawet gdybym ją miał.Istnieje funkcja biblioteki matematycznej C99 o nazwie copysign (), która pobiera znak z jednego argumentu, a wartość bezwzględną z drugiego:
da wynik +/- 1,0, w zależności od znaku wartości. Zauważ, że zera zmiennoprzecinkowe są podpisane: (+0) da +1, a (-0) da -1.
źródło
Wygląda na to, że większość odpowiedzi pominęła pierwotne pytanie.
Nie ma go w standardowej bibliotece, jednak istnieje taki,
copysign
który może być używany prawie w ten sam sposób,copysign(1.0, arg)
i jest w nim funkcja prawdziwego znakuboost
, która równie dobrze może być częścią standardu.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
źródło
Najwyraźniej odpowiedź na pytanie pierwotnego plakatu brzmi „nie”. Nie ma standardowej
sgn
funkcji C ++ .źródło
copysign()
nie ustawi pierwszego parametru na 0.0, jeśli drugi to 0.0. Innymi słowy, John ma rację.Szybsze niż powyższe rozwiązania, w tym najwyżej ocenione:
źródło
Tak, w zależności od definicji.
C99 i nowsze wersje mają
signbit()
makro w<math.h>
Jednak OP chce czegoś nieco innego.
Głębiej:
Post nie jest specyficzny w następujących przypadkach:
x = 0.0, -0.0, +NaN, -NaN
.Klasyczny
signum()
powraca+1
onx>0
,-1
onx<0
i0
onx==0
.Wiele odpowiedzi już to obejmowało, ale nie zawiera odpowiedzi
x = -0.0, +NaN, -NaN
. Wiele z nich jest nastawionych na całkowity punkt widzenia, w którym zwykle brakuje Not-a-Numbers ( NaN ) i -0,0 .Typowe odpowiedzi działają jak
signnum_typical()
On-0.0, +NaN, -NaN
, zwracają0.0, 0.0, 0.0
.Zamiast tego proponuję tę funkcjonalność: On
-0.0, +NaN, -NaN
zwraca-0.0, +NaN, -NaN
.źródło
Jest sposób na zrobienie tego bez rozgałęziania, ale nie jest to zbyt ładne.
http://graphics.stanford.edu/~seander/bithacks.html
Na tej stronie jest też wiele innych interesujących, zbyt sprytnych rzeczy ...
źródło
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
lubsign = (v > 0) - (v < 0);
.v
jest to liczba całkowita nie szersza niż intJeśli wszystko, czego chcesz, to przetestować znak, użyj signbit (zwraca true, jeśli jego argument ma znak ujemny). Nie jestem pewien, dlaczego szczególnie chcesz zwrócić -1 lub +1; copysign jest do tego wygodniejszy, ale wygląda na to, że zwróci +1 dla ujemnego zera na niektórych platformach z jedynie częściowym wsparciem dla ujemnego zera, gdzie signbit prawdopodobnie zwróci prawdę.
źródło
if (x < 0)
.Ogólnie rzecz biorąc, w C / C ++ nie ma standardowej funkcji signum, a brak tak fundamentalnej funkcji mówi wiele o tych językach.
Poza tym uważam, że oba poglądy większości na temat właściwego podejścia do zdefiniowania takiej funkcji są w pewnym sensie poprawne, a „kontrowersja” na ten temat jest w rzeczywistości pozbawiona argumentów, gdy weźmie się pod uwagę dwa ważne zastrzeżenia:
Funkcja signum powinna zawsze zwracać typ swojego argumentu, podobnie jak
abs()
funkcja, ponieważ signum jest zwykle używane do mnożenia z wartością bezwzględną po tym, jak ta ostatnia zostanie jakoś przetworzona. Dlatego głównym przykładem użycia signum nie są porównania, ale arytmetyka, a ta ostatnia nie powinna obejmować żadnych kosztownych konwersji liczb całkowitych na zmiennoprzecinkowe.Typy zmiennoprzecinkowe nie mają pojedynczej dokładnej wartości zerowej: +0,0 można interpretować jako „nieskończenie poniżej zera”, a -0,0 jako „nieskończenie poniżej zera”. To jest powód, dla którego porównania obejmujące zero muszą wewnętrznie sprawdzać obie wartości, a wyrażenie takie
x == 0.0
może być niebezpieczne.Jeśli chodzi o C, myślę, że najlepszym rozwiązaniem w przypadku typów integralnych jest rzeczywiście użycie
(x > 0) - (x < 0)
wyrażenia, ponieważ powinno być przetłumaczone w sposób bezoddziałowy i wymaga tylko trzech podstawowych operacji. Najlepiej zdefiniuj funkcje wbudowane, które wymuszają typ zwracany zgodny z typem argumentu, i dodaj C11,define _Generic
aby odwzorować te funkcje na wspólną nazwę.Jeśli chodzi o wartości zmiennoprzecinkowe, myślę, że funkcje wbudowane oparte na C11
copysignf(1.0f, x)
,copysign(1.0, x)
icopysignl(1.0l, x)
są właściwą drogą, po prostu dlatego, że są one również wysoce wolne od rozgałęzień i dodatkowo nie wymagają rzutowania wyniku z liczby całkowitej z powrotem na zmiennoprzecinkowy wartość. Prawdopodobnie powinieneś wyraźnie skomentować, że twoje implementacje zmiennoprzecinkowe signum nie zwrócą zera ze względu na specyfikę wartości zmiennoprzecinkowych zera, względy czasu przetwarzania, a także dlatego, że często jest bardzo przydatne w arytmetyki zmiennoprzecinkowej, aby otrzymać poprawne -1 / + 1 znak, nawet dla wartości zerowych.źródło
Moja kopia C w pigułce ujawnia istnienie standardowej funkcji o nazwie copysign, która może być przydatna. Wygląda na to, że copysign (1.0, -2.0) zwróci -1.0, a copysign (1.0, 2.0) zwróci +1.0.
Całkiem blisko co?
źródło
Nie, nie istnieje w c ++, jak w Matlabie. W tym celu używam makra.
źródło
#define sign(x) (((x) > 0) - ((x) < 0))
co też jest dobre.Przyjęta odpowiedź z poniższym przeciążeniem faktycznie nie wyzwala limitów -Wtype .
Dla C ++ 11 może być alternatywą.
Dla mnie nie wywołuje żadnych ostrzeżeń w GCC 5.3.1.
źródło
-Wunused-parameter
ostrzeżenia, użyj nienazwanych parametrów.Trochę nie na temat, ale używam tego:
i znalazłem pierwszą funkcję - tę z dwoma argumentami, która jest znacznie bardziej użyteczna ze „standardowej” funkcji sgn (), ponieważ najczęściej jest używana w takim kodzie:
vs.
nie ma obsady dla niepodpisanych typów i żadnego dodatkowego minusa.
w rzeczywistości mam ten fragment kodu za pomocą sgn ()
źródło
Pytanie jest stare, ale istnieje teraz taka pożądana funkcja. Dodałem owijarkę z not, left shift i dec.
Możesz użyć funkcji otoki opartej na signbit z C99 , aby uzyskać dokładnie pożądane zachowanie (zobacz kod poniżej).
Uwaga: używam operandu nie („!”), Ponieważ wartość zwrotna signbit nie jest określona jako 1 (chociaż przykłady pozwalają sądzić, że zawsze tak będzie), ale prawda dla liczby ujemnej:
Następnie mnożę przez dwa z przesunięciem w lewo („<< 1”), co da nam 2 dla liczby dodatniej i 0 dla liczby ujemnej, a na koniec zmniejszamy o 1, aby uzyskać 1 i -1 odpowiednio dla liczb dodatnich i ujemnych zgodnie z żądaniem OP.
źródło
Chociaż całkowite rozwiązanie w przyjętej odpowiedzi jest dość eleganckie, przeszkadzało mi, że nie będzie w stanie zwrócić NAN dla podwójnych typów, więc nieco go zmodyfikowałem.
Zauważ, że zwracanie zmiennoprzecinkowej sieci NAN w przeciwieństwie do zakodowanego
NAN
na stałe powoduje, że bit znaku jest ustawiany w niektórych implementacjach , więc wyjście dlaval = -NAN
ival = NAN
będzie identyczne bez względu na wszystko (jeśli wolisznan
wyjście " " niż-nan
możeszabs(val)
przed powrotem ...)źródło
Możesz użyć
boost::math::sign()
metody,boost/math/special_functions/sign.hpp
jeśli dostępne jest wzmocnienie.źródło
Oto implementacja przyjazna dla rozgałęzień:
O ile twoje dane nie mają zer jako połowy liczb, tutaj predyktor gałęzi wybierze jedną z gałęzi jako najbardziej powszechną. Oba oddziały wymagają jedynie prostych operacji.
Alternatywnie, w niektórych kompilatorach i architekturach procesorów wersja całkowicie bez rozgałęzień może być szybsza:
Działa to w przypadku binarnego zmiennoprzecinkowego formatu podwójnej precyzji IEEE 754: binary64 .
źródło
Ta funkcja zakłada:
źródło
copysign
; jeśli używaszstatic_assert
, masz C ++ 11 i równie dobrze możesz go użyćcopysign
.źródło
Po co używać operatorów trójskładnikowych i if-else, skoro można to po prostu zrobić
źródło
x == INT_MIN
.