Czy w C / C ++ jest standardowa funkcja znaku (signum, sgn)?

409

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.

zwariowany
źródło
13
Co powinien zwrócić za 0?
Craig McQueen,
61
@Craig McQueen; zależy to od tego, czy jest to zero dodatnie czy ujemne zero.
ysth
1
Zauważyłem, że podałeś wartość zwracaną jako liczbę całkowitą. Szukasz rozwiązania, które przyjmuje liczby całkowite lub zmiennoprzecinkowe?
Mark Byers,
6
@ysth @Craig McQueen, fałsz również dla pływaków, nie? Definicja sgn (x) mówi, aby zwrócić 0 jeśli x==0. Zgodnie z IEEE 754 zero ujemne i zero dodatnie powinny być równe.
RJFalconer
5
@ysth „zależy od dodatniego zera lub ujemnego zera”. W rzeczywistości tak nie jest.
RJFalconer,

Odpowiedzi:

506

Zaskoczony, nikt jeszcze nie opublikował bezpiecznej wersji C ++:

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

Korzyści:

  • W rzeczywistości implementuje signum (-1, 0 lub 1). Implementacje przy użyciu copysign zwracają tylko -1 lub 1, co nie jest signum. Ponadto niektóre implementacje zwracają liczbę zmiennoprzecinkową (lub T) zamiast int, co wydaje się marnotrawstwem.
  • Działa dla liczb całkowitych, liczb zmiennoprzecinkowych, podwójnych, niepodpisanych skrótów lub dowolnych niestandardowych typów możliwych do zbudowania z liczby całkowitej 0 i zamówienia.
  • Szybki! copysignjest powolny, szczególnie jeśli musisz się promować, a następnie ponownie zawęzić. Jest to bez rozgałęzień i doskonale optymalizuje
  • Zgodny ze standardami! Hack bitshift jest czysty, ale działa tylko w przypadku niektórych reprezentacji bitowych i nie działa, gdy masz typ bez znaku. W razie potrzeby można ją podać jako specjalizację ręczną.
  • Dokładny! Proste porównania z zerem mogą zachować wewnętrzną, precyzyjną reprezentację maszyny (np. 80 bitów na x87) i uniknąć przedwczesnego zaokrąglenia do zera.

Ostrzeżenia:

  • Jest to szablon, więc w niektórych okolicznościach kompilacja może potrwać dłużej.
  • Najwyraźniej niektórzy uważają, że użycie nowej, nieco ezoterycznej i bardzo powolnej standardowej funkcji biblioteki , która nawet nie implementuje signum, jest bardziej zrozumiałe.
  • < 0Częścią kontroli wyzwala GCC -Wtype-limitsostrzeżenie, gdy instancja dla typu unsigned. Można tego uniknąć, stosując pewne przeciążenia:

    template <typename T> inline constexpr
    int signum(T x, std::false_type is_signed) {
        return T(0) < x;
    }
    
    template <typename T> inline constexpr
    int signum(T x, std::true_type is_signed) {
        return (T(0) < x) - (x < T(0));
    }
    
    template <typename T> inline constexpr
    int signum(T x) {
        return signum(x, std::is_signed<T>());
    }

    (Co jest dobrym przykładem pierwszego zastrzeżenia).

Pharap
źródło
18
@GMan: GCC dopiero teraz (4.5) przestało mieć kwadratowy koszt w stosunku do liczby instancji funkcji szablonów, a ich parsowanie i tworzenie jest znacznie droższe niż w przypadku funkcji pisanych ręcznie lub standardowego preprocesora C. Linker musi również wykonać więcej pracy, aby usunąć zduplikowane instancje. Szablony zachęcają także do włączania # obejmuje-w #, co powoduje, że obliczanie zależności zajmuje więcej czasu i niewielkie zmiany (często implementacja, a nie interfejs) w celu wymuszenia rekompilacji większej liczby plików.
15
@Joe: Tak, i nadal nie ma zauważalnych kosztów. C ++ używa szablonów, to jest coś, co wszyscy musimy zrozumieć, zaakceptować i przejść.
GManNickG
42
Zaraz, co to za biznes typu „copysign is slow”…? Korzystanie z obecnych kompilatorów (g ++ 4.6+, clang ++ 3.0) std::copysignwydaje 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) ...
snogglethorpe 23'12
14
@snogglethorpe: Jeśli wywołujesz copysignint, 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.
53
Pierwsza wersja nie jest bez rozgałęzień. Dlaczego ludzie myślą, że porównanie użyte w wyrażeniu nie wygeneruje gałęzi? Będzie na większości architektur. Tylko procesory, które mają cmove (lub predykację) wygenerują kod bez rozgałęzień, ale zrobią to również dla trójek lub jeśli / inaczej, jeśli jest to wygrana.
Patrick Schlüter,
271

Nie znam standardowej funkcji do tego. Oto ciekawy sposób, aby to napisać:

(x > 0) - (x < 0)

Oto bardziej czytelny sposób:

if (x > 0) return 1;
if (x < 0) return -1;
return 0;

Jeśli podoba Ci się operator trójskładnikowy, możesz to zrobić:

(x > 0) ? 1 : ((x < 0) ? -1 : 0)
Mark Byers
źródło
7
Mark Ransom, twoje wyrażenia dają nieprawidłowe wyniki dla x==0.
avakar
3
@Svante: „Każdy z operatorów <, >... da 1, jeśli określona relacja jest prawdziwa, i 0, jeśli jest fałszywa”
Stephen Canon,
11
@Svante: niezupełnie. Wartość 0„false”; każda inna wartość jest „prawdziwa”; jednak operatory relacji i równości zawsze zwracają 0lub 1(patrz Norma 6.5.8 i 6.5.9). - wartość wyrażenia a * (x == 42)jest albo 0czy a.
pmg
21
Znak wysokiej wydajności, jestem zaskoczony, że przegapiłeś tag C ++. Ta odpowiedź jest bardzo ważna i nie zasługuje na głosowanie w dół. Co więcej, nie użyłbym copysigncałki, xnawet gdybym ją miał.
avakar
6
Czy ktoś faktycznie sprawdził, jaki kod GCC / G ++ / jakikolwiek inny kompilator emituje na prawdziwej platformie? Domyślam się, że wersja „bezgałęziowa” używa dwóch gałęzi zamiast jednej. Bitshifting jest prawdopodobnie znacznie szybszy - i bardziej przenośny pod względem wydajności.
Jørgen Fogh
192

Istnieje funkcja biblioteki matematycznej C99 o nazwie copysign (), która pobiera znak z jednego argumentu, a wartość bezwzględną z drugiego:

result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double

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.

nadchodząca burza
źródło
57
Poparłem tę, przegłosowałem najpopularniejszą odpowiedź. Pozostawiony w zdumieniu, że społeczność SO wydaje się preferować hack niż korzystanie ze standardowej funkcji biblioteki. Niech bogowie programowania skazują was wszystkich na rozszyfrowanie włamań używanych przez sprytnych programistów niezaznajomionych ze standardami językowymi. Tak, wiem, że to kosztuje mnie mnóstwo powtórzeń na SO, ale wolałbym stawić czoła nadchodzącej burzy niż reszcie z was ...
High Performance Mark
34
Jest blisko, ale daje złą odpowiedź na zero (przynajmniej zgodnie z artykułem Wikipedii w pytaniu). Fajna sugestia. W każdym razie +1.
Mark Byers,
4
Jeśli chcesz liczbę całkowitą lub dokładny wynik dla zer, podoba mi się odpowiedź Marka Byersa, która jest diabelnie elegancka! Jeśli nie przejmujesz się powyższym, copysign () może mieć przewagę wydajności, w zależności od aplikacji - gdybym optymalizował pętlę krytyczną, spróbowałbym obu.
nadchodząca burza
10
1) C99 nie jest w pełni obsługiwany wszędzie (rozważ VC ++); 2) jest to również pytanie C ++. To dobra odpowiedź, ale ta pozytywna również działa i ma szersze zastosowanie.
Pavel Minaev
5
Zbawiciel! Potrzebował sposobu na określenie między -0,0 a 0,0
Ólafur Waage
79

Wygląda na to, że większość odpowiedzi pominęła pierwotne pytanie.

Czy w C / C ++ jest standardowa funkcja znaku (signum, sgn)?

Nie ma go w standardowej bibliotece, jednak istnieje taki, copysignktóry może być używany prawie w ten sam sposób, copysign(1.0, arg)i jest w nim funkcja prawdziwego znaku boost, która równie dobrze może być częścią standardu.

    #include <boost/math/special_functions/sign.hpp>

    //Returns 1 if x > 0, -1 if x < 0, and 0 if x is zero.
    template <class T>
    inline int sign (const T& z);

http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html

Catskul
źródło
5
To powinna być najczęściej głosowana odpowiedź, ponieważ daje możliwie najbliższe rozwiązanie tego, o co pytano w pytaniu.
BartoszKP,
Przez ostatnie kilka minut zastanawiałem się, dlaczego biblioteka standardowa nie ma funkcji podpisywania. Jest to tak powszechne - zdecydowanie częściej używane niż funkcja gamma, którą można znaleźć w nagłówku cmath.
Taozi
4
Wyjaśnienie, które często otrzymuję dla podobnych pytań, brzmi: „dość łatwo jest się samemu wdrożyć”. Która IMO nie jest dobrym powodem. Całkowicie zaprzecza problemom związanym z tym, gdzie standaryzacja, nieoczywiste przypadki krawędziowe i gdzie umieścić tak szeroko stosowane narzędzie.
Catskul
77

Najwyraźniej odpowiedź na pytanie pierwotnego plakatu brzmi „nie”. Nie ma standardowejsgn funkcji C ++ .

Jan
źródło
2
@SR_ Nie masz racji. copysign()nie ustawi pierwszego parametru na 0.0, jeśli drugi to 0.0. Innymi słowy, John ma rację.
Alexis Wilke
29

Szybsze niż powyższe rozwiązania, w tym najwyżej ocenione:

(x < 0) ? -1 : (x > 0)
xnx
źródło
1
Jakiego typu jest x? A może używasz #define?
Szansa
3
Twój typ nie jest szybszy. Spowoduje to dość częste chybienie w pamięci podręcznej.
Jeffrey Drake,
19
Pamięć podręczna? Nie jestem pewien jak. Być może miałeś na myśli nieporozumienie oddziału?
Catskul
2
Wydaje mi się, że spowoduje to ostrzeżenie o mylących liczbach całkowitych i boolowskich!
sergiol
jak to będzie szybko z oddziałem?
Nick
29

Czy w C / C ++ jest standardowa funkcja znaku (signum, sgn)?

Tak, w zależności od definicji.

C99 i nowsze wersje mają signbit()makro w<math.h>

int signbit(real-floating x);
W signbitmakro zwraca wartość różną od zera, wtedy i tylko wtedy, gdy znak jego wartości argumentu jest ujemna. C11 §7.12.3.6


Jednak OP chce czegoś nieco innego.

Chcę funkcji, która zwraca -1 dla liczb ujemnych i +1 dla liczb dodatnich. ... funkcja działająca na pływakach.

#define signbit_p1_or_n1(x)  ((signbit(x) ?  -1 : 1)

Głębiej:

Post nie jest specyficzny w następujących przypadkach: x = 0.0, -0.0, +NaN, -NaN.

Klasyczny signum()powraca +1on x>0, -1on x<0i 0on x==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.

int signnum_typical(double x) {
  if (x > 0.0) return 1;
  if (x < 0.0) return -1;
  return 0;
}

Zamiast tego proponuję tę funkcjonalność: On -0.0, +NaN, -NaNzwraca -0.0, +NaN, -NaN.

double signnum_c(double x) {
  if (x > 0.0) return 1.0;
  if (x < 0.0) return -1.0;
  return x;
}
chux - Przywróć Monikę
źródło
1
Ach, dokładnie o to mi chodzi. To się właśnie zmieniło w Pharo Smalltalk github.com/pharo-project/pharo/pull/1835 i zastanawiałem się, czy istnieje jakiś standard (IEC 60559 lub ISO 10967) dyktujący zachowanie dla zachowania ujemnego zera i nan ... javascript sign developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
aka.nice
16

Jest sposób na zrobienie tego bez rozgałęziania, ale nie jest to zbyt ładne.

sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));

http://graphics.stanford.edu/~seander/bithacks.html

Na tej stronie jest też wiele innych interesujących, zbyt sprytnych rzeczy ...

Tim Sylvester
źródło
1
Jeśli poprawnie odczytam link, który zwraca tylko -1 lub 0. Jeśli chcesz -1, 0 lub +1, to jest sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));lub sign = (v > 0) - (v < 0);.
Z bozonem
1
oznacza to, że vjest to liczba całkowita nie szersza niż int
phuclv
12

Jeś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ę.

ysth
źródło
7
Istnieje wiele aplikacji matematycznych, w których znak (x) jest konieczny. W przeciwnym razie po prostu bym to zrobił if (x < 0).
szansa
5

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.0moż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 _Genericaby 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)i copysignl(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.

Tabernakel
źródło
5

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?

Znak wysokiej wydajności
źródło
Niestandardowy, ale może być powszechnie dostępny. Microsoft zaczyna się od podkreślenia, czyli konwencji, której używają w przypadku niestandardowych rozszerzeń. Nie jest to jednak najlepszy wybór, gdy pracujesz z liczbami całkowitymi.
Mark Ransom,
5
copysign jest zgodny ze standardami ISO C (C99) i POSIX. Zobacz opengroup.org/onlinepubs/000095399/functions/copysign.html
lhf
3
Co powiedziałem. Visual Studio nie jest odniesieniem do standardu C.
Stephen Canon
3

Nie, nie istnieje w c ++, jak w Matlabie. W tym celu używam makra.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
paplanie
źródło
5
W C ++ należy preferować szablony niż makra.
Ruslan
W C nie ma szablonu ...... helloacm.com/how-to-implement-the-sgn-function-in-c
doctorlai 20.10.2016
Pomyślałem, że to dobra odpowiedź, a potem spojrzałem na własny kod i znalazłem to: #define sign(x) (((x) > 0) - ((x) < 0))co też jest dobre.
Michel Rouzic,
1
funkcja inline jest lepsza niż makro w C, a w C ++ szablon jest lepszy
phuclv 10.04.18
3

Przyjęta odpowiedź z poniższym przeciążeniem faktycznie nie wyzwala limitów -Wtype .

template <typename T> inline constexpr
  int signum(T x, std::false_type) {
  return T(0) < x;
}

template <typename T> inline constexpr
  int signum(T x, std::true_type) {
  return (T(0) < x) - (x < T(0));
}

template <typename T> inline constexpr
  int signum(T x) {
  return signum(x, std::is_signed<T>());
}

Dla C ++ 11 może być alternatywą.

template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, int>::type
inline constexpr signum(T const x) {
    return T(0) < x;  
}

template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T const x) {
    return (T(0) < x) - (x < T(0));  
}

Dla mnie nie wywołuje żadnych ostrzeżeń w GCC 5.3.1.

SamVanDonut
źródło
Aby uniknąć -Wunused-parameterostrzeżenia, użyj nienazwanych parametrów.
Jonathan Wakely
To jest naprawdę bardzo prawdziwe. Tęsknie za tym. Jednak bardziej podoba mi się alternatywa dla C ++ 11.
SamVanDonut
2

Trochę nie na temat, ale używam tego:

template<typename T>
constexpr int sgn(const T &a, const T &b) noexcept{
    return (a > b) - (a < b);
}

template<typename T>
constexpr int sgn(const T &a) noexcept{
    return sgn(a, T(0));
}

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:

int comp(unsigned a, unsigned b){
   return sgn( int(a) - int(b) );
}

vs.

int comp(unsigned a, unsigned b){
   return sgn(a, b);
}

nie ma obsady dla niepodpisanych typów i żadnego dodatkowego minusa.

w rzeczywistości mam ten fragment kodu za pomocą sgn ()

template <class T>
int comp(const T &a, const T &b){
    log__("all");
    if (a < b)
        return -1;

    if (a > b)
        return +1;

    return 0;
}

inline int comp(int const a, int const b){
    log__("int");
    return a - b;
}

inline int comp(long int const a, long int const b){
    log__("long");
    return sgn(a, b);
}
Nacięcie
źródło
1

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).

Zwraca, czy znak x jest ujemny.
Można to również zastosować do nieskończoności, NaN i zer (jeśli zero jest bez znaku, jest uważane za dodatnie

#include <math.h>

int signValue(float a) {
    return ((!signbit(a)) << 1) - 1;
}

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:

Wartość zwracana Wartość
niezerowa (prawda), jeśli znak x jest ujemny; i zero (fałsz) w przeciwnym razie.

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.

Antonin GAVREL
źródło
0 też będzie dodatnia ... co może lub nie być tym, czego chciał OP ...
Antti Haapala
cóż, możemy nigdy nie wiedzieć, czego OP naprawdę chciałby, jeśli n = 0 ...!
Antonin GAVREL
0

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.

template <typename T> double sgn(T val) {
    return double((T(0) < val) - (val < T(0)))/(val == val);
}

Zauważ, że zwracanie zmiennoprzecinkowej sieci NAN w przeciwieństwie do zakodowanego NANna stałe powoduje, że bit znaku jest ustawiany w niektórych implementacjach , więc wyjście dla val = -NANi val = NANbędzie identyczne bez względu na wszystko (jeśli wolisz nanwyjście " " niż -nanmożesz abs(val)przed powrotem ...)

mrclng
źródło
0

Możesz użyć boost::math::sign()metody, boost/math/special_functions/sign.hppjeśli dostępne jest wzmocnienie.

Khkarens
źródło
Zauważ, że zasugerowano to wcześniej: stackoverflow.com/a/16869019/1187415 .
Martin R
0

Oto implementacja przyjazna dla rozgałęzień:

inline int signum(const double x) {
    if(x == 0) return 0;
    return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

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:

inline int signum(const double x) {
    return (x != 0) * 
        (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

Działa to w przypadku binarnego zmiennoprzecinkowego formatu podwójnej precyzji IEEE 754: binary64 .

Serge Rogatch
źródło
-1
int sign(float n)
{     
  union { float f; std::uint32_t i; } u { n };
  return 1 - ((u.i >> 31) << 1);
}

Ta funkcja zakłada:

  • binarny32 reprezentacja liczb zmiennoprzecinkowych
  • kompilator, który robi wyjątek od ścisłej reguły aliasingu podczas używania nazwanego związku
Gigi
źródło
3
Nadal istnieją tutaj złe założenia. Na przykład nie wierzę, że endianness typu float jest gwarantowany jako endianness liczby całkowitej. Kontrola nie powiedzie się również w przypadku wszystkich architektur korzystających z ILP64. Naprawdę, tylko reimplementujesz copysign; jeśli używasz static_assert, masz C ++ 11 i równie dobrze możesz go użyć copysign.
-3
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
cyberion
źródło
-3

Po co używać operatorów trójskładnikowych i if-else, skoro można to po prostu zrobić

#define sgn(x) x==0 ? 0 : x/abs(x)
Jagreet
źródło
3
Twoja definicja używa również operatora trójskładnikowego.
Martin R
Tak, zdecydowanie, ale używa tylko jednego trójskładnikowego operatora do oddzielenia liczb zerowych i niezerowych. Inne wersje zawierają zagnieżdżone potrójne operacje oddzielające dodatnie, ujemne i zero.
Jagreet
Użycie podziału na liczby całkowite jest bardzo nieefektywne, a abs () dotyczy tylko liczb całkowitych.
Michel Rouzic,
Nieokreślone zachowanie możliwe, gdy x == INT_MIN.
chux - Przywróć Monikę