Gibt es eine Standardzeichenfunktion (signum, sgn) in C / C ++?

409

Ich möchte eine Funktion, die -1 für negative Zahlen und +1 für positive Zahlen zurückgibt. http://en.wikipedia.org/wiki/Sign_function Es ist einfach genug, meine eigenen zu schreiben, aber es scheint etwas zu sein, das irgendwo in einer Standardbibliothek sein sollte.

Bearbeiten: Insbesondere suchte ich nach einer Funktion, die an Floats arbeitet.

bekloppt
quelle
13
Was soll es für 0 zurückgeben?
Craig McQueen
61
@Craig McQueen; Das hängt davon ab, ob es sich um eine positive oder eine negative Null handelt.
ysth
1
Ich habe festgestellt, dass Sie den Rückgabewert als Ganzzahl angegeben haben. Suchen Sie nach einer Lösung, die Ganzzahlen oder Gleitkommazahlen akzeptiert?
Mark Byers
6
@ysth @Craig McQueen, falsch auch für Floats, nein? Die Definition von sgn (x) besagt, dass 0 zurückgegeben werden soll, wenn x==0. Gemäß IEEE 754 sollten negative Null und positive Null als gleich verglichen werden.
RJFalconer
5
@ysth "es hängt von der positiven Null oder der negativen Null ab". In der Tat nicht.
RJFalconer

Antworten:

506

Überrascht hat noch niemand die typsichere C ++ - Version veröffentlicht:

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

Leistungen:

  • Implementiert tatsächlich signum (-1, 0 oder 1). Implementierungen, die hier Copysign verwenden, geben nur -1 oder 1 zurück, was kein Signum ist. Außerdem geben einige Implementierungen hier eher einen Float (oder T) als einen Int zurück, was verschwenderisch erscheint.
  • Funktioniert für Ints, Floats, Doubles, Shorts ohne Vorzeichen oder alle benutzerdefinierten Typen, die aus Ganzzahl 0 konstruierbar und bestellbar sind.
  • Schnell! copysignist langsam, besonders wenn Sie fördern und dann wieder verengen müssen. Dies ist verzweigungslos und optimiert hervorragend
  • Normkonform! Der Bitshift-Hack ist ordentlich, funktioniert aber nur für einige Bit-Darstellungen und nicht, wenn Sie einen vorzeichenlosen Typ haben. Es könnte gegebenenfalls als manuelle Spezialisierung bereitgestellt werden.
  • Genau! Durch einfache Vergleiche mit Null kann die interne hochpräzise Darstellung der Maschine (z. B. 80 Bit auf x87) beibehalten und eine vorzeitige Runde auf Null vermieden werden.

Vorsichtsmaßnahmen:

  • Da es sich um eine Vorlage handelt, kann das Kompilieren unter bestimmten Umständen länger dauern.
  • Anscheinend halten einige Leute die Verwendung einer neuen, etwas esoterischen und sehr langsamen Standardbibliotheksfunktion , die signum nicht einmal wirklich implementiert, für verständlicher.
  • Der < 0Teil der Prüfung löst die -Wtype-limitsWarnung des GCC aus, wenn er für einen Typ ohne Vorzeichen instanziiert wird. Sie können dies vermeiden, indem Sie einige Überladungen verwenden:

    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>());
    }

    (Welches ist ein gutes Beispiel für die erste Einschränkung.)

Pharap
quelle
18
@GMan: GCC hat erst jetzt (4.5) aufgehört, die Kosten quadratisch zur Anzahl der Instanziierungen für Vorlagenfunktionen zu haben, und das Parsen und Instanziieren ist immer noch drastisch teurer als manuell geschriebene Funktionen oder der Standard-C-Präprozessor. Der Linker muss auch mehr Arbeit leisten, um doppelte Instanziierungen zu entfernen. Vorlagen fördern auch # Includes-In # Includes, wodurch die Abhängigkeitsberechnung länger dauert und kleine (häufig Implementierung, keine Schnittstelle) Änderungen erforderlich sind, um die Neukompilierung weiterer Dateien zu erzwingen.
15
@ Joe: Ja, und es gibt immer noch keine spürbaren Kosten. C ++ verwendet Vorlagen, das müssen wir alle verstehen, akzeptieren und hinter uns bringen.
GManNickG
42
Warten Sie, was ist das für ein "Copysign ist langsam" -Geschäft ...? Die Verwendung aktueller Compiler (g ++ 4.6+, clang ++ 3.0) std::copysignscheint für mich einen hervorragenden Code zu ergeben: 4 Anweisungen (inline), keine Verzweigung, ausschließlich unter Verwendung der FPU. Das Rezept in dieser Antwort erzeugt dagegen einen viel schlechteren Code (viel mehr Anweisungen, einschließlich einer Multiplikation, die sich zwischen einer ganzzahligen Einheit und einer FPU hin und her bewegt) ...
snogglethorpe
14
@snogglethorpe: Wenn Sie copysignein int aufrufen, wird es zu float / double befördert und muss bei der Rückkehr wieder enger werden. Ihr Compiler kann diese Promotion optimieren, aber ich kann nichts finden, was darauf hindeutet, dass dies durch den Standard garantiert wird. Um signum über Copysign zu implementieren, müssen Sie den Fall 0 manuell behandeln. Stellen Sie sicher, dass Sie dies in jeden Leistungsvergleich einbeziehen.
53
Die erste Version ist nicht verzweigungslos. Warum glauben die Leute, dass ein in einem Ausdruck verwendeter Vergleich keinen Zweig erzeugt? Es wird auf den meisten Architekturen. Nur Prozessoren, die einen cmove (oder eine Prädikation) haben, generieren verzweigungslosen Code, aber sie tun dies auch für Ternaries oder wenn / sonst wenn es ein Gewinn ist.
Patrick Schlüter
271

Ich kenne keine Standardfunktion dafür. Hier ist eine interessante Möglichkeit, es zu schreiben:

(x > 0) - (x < 0)

Hier ist eine besser lesbare Möglichkeit:

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

Wenn Sie den ternären Operator mögen, können Sie dies tun:

(x > 0) ? 1 : ((x < 0) ? -1 : 0)
Mark Byers
quelle
7
Mark Ransom, Ihre Ausdrücke liefern falsche Ergebnisse für x==0.
Avakar
3
@Svante: "Jeder der Operatoren <, >... soll 1 ergeben, wenn die angegebene Beziehung wahr ist, und 0, wenn sie falsch ist"
Stephen Canon
11
@Svante: nicht genau. Ein Wert von 0ist "false"; jeder andere Wert ist "wahr"; Die relationalen und Gleichheitsoperatoren geben jedoch immer 0oder zurück 1(siehe Standard 6.5.8 und 6.5.9). - Der Wert des Ausdrucks a * (x == 42)ist entweder 0oder a.
pmg
21
High-Performance Mark, ich bin erstaunt, dass Sie das C ++ - Tag verpasst haben. Diese Antwort ist sehr gültig und verdient keine Abwertung. Außerdem würde ich nicht copysignfür Integral verwenden, xselbst wenn ich es zur Verfügung hätte.
Avakar
6
Hat jemand tatsächlich überprüft, welchen Code GCC / G ++ / irgendein anderer Compiler auf einer realen Plattform ausgibt? Ich vermute, dass die "branchless" -Version zwei Zweige anstelle von einem verwendet. Bitshifting ist wahrscheinlich viel schneller - und in Bezug auf die Leistung tragbarer.
Jørgen Fogh
192

Es gibt eine C99-Mathematikbibliotheksfunktion namens copysign (), die das Vorzeichen von einem Argument und den Absolutwert von dem anderen übernimmt:

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

gibt Ihnen ein Ergebnis von +/- 1,0, abhängig vom Vorzeichen des Wertes. Beachten Sie, dass Gleitkomma-Nullen vorzeichenbehaftet sind: (+0) ergibt +1 und (-0) ergibt -1.

kommender Sturm
quelle
57
Upvoted diese, downvoted beliebteste Antwort. Ich bin erstaunt darüber, dass die SO-Community einen Hack der Verwendung einer Standardbibliotheksfunktion vorzuziehen scheint. Mögen die Götter der Programmierung Sie alle dazu verurteilen, Hacks zu entschlüsseln, die von cleveren Programmierern verwendet werden, die mit Sprachstandards nicht vertraut sind. Ja, ich weiß, dass mich das eine Menge Wiederholungen auf SO kosten wird, aber ich würde lieber auf der Seite des kommenden Sturms stehen als der Rest von euch ...
High Performance Mark
34
Dies ist nah, aber es gibt die falsche Antwort für Null (laut Wikipedia-Artikel in der Frage zumindest). Netter Vorschlag. +1 sowieso.
Mark Byers
4
Wenn Sie eine ganze Zahl wollen oder wenn Sie das genaue Signumergebnis für Nullen wollen, mag ich die Antwort von Mark Byers, die teuflisch elegant ist! Wenn Sie sich nicht für das oben Genannte interessieren, kann copysign () je nach Anwendung einen Leistungsvorteil haben. Wenn ich eine kritische Schleife optimieren würde, würde ich beides versuchen.
Comingstorm
10
1) C99 wird nicht überall vollständig unterstützt (siehe VC ++); 2) Dies ist auch eine C ++ - Frage. Dies ist eine gute Antwort, aber die hochgestimmte funktioniert auch und ist allgemeiner anwendbar.
Pavel Minaev
5
Retter! Benötigte einen Weg, um zwischen -0,0 und 0,0 zu bestimmen
Ólafur Waage
79

Es scheint, dass die meisten Antworten die ursprüngliche Frage verfehlt haben.

Gibt es eine Standardzeichenfunktion (signum, sgn) in C / C ++?

Nicht in der Standardbibliothek, aber es gibt copysigneine, die fast auf die gleiche Weise über verwendet werden kann, copysign(1.0, arg)und es gibt eine True-Sign-Funktion in boost, die genauso gut Teil des Standards sein könnte.

    #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
quelle
5
Dies sollte die am häufigsten gewählte Antwort sein, da sie die bestmögliche Lösung für die in der Frage gestellten Fragen bietet.
BartoszKP
Ich habe mich in den letzten Minuten gefragt, warum die Standardbibliothek keine Vorzeichenfunktion hat. Es ist einfach so häufig - definitiv häufiger als die Gammafunktion, die im cmath-Header zu finden ist.
Taozi
4
Die Erklärung, die ich oft für ähnliche Fragen bekomme, lautet: "Es ist einfach genug, sich selbst zu implementieren." Welche IMO ist kein guter Grund. Es widerlegt völlig die Probleme, wo Standardisierung, nicht offensichtliche Randfälle und wo ein so weit verbreitetes Werkzeug zu platzieren ist.
Catskul
77

Anscheinend lautet die Antwort auf die Frage des Originalplakats nein. Es gibt keine Standard- C ++ - sgnFunktion.

John
quelle
2
@SR_ Du bist nicht korrekt. copysign()macht Ihren ersten Parameter nicht 0.0, wenn der zweite 0.0 ist. Mit anderen Worten, John ist richtig.
Alexis Wilke
29

Schneller als die oben genannten Lösungen, einschließlich der am besten bewerteten:

(x < 0) ? -1 : (x > 0)
xnx
quelle
1
Welcher Typ ist x? Oder verwenden Sie ein #define?
Chance
3
Ihr Typ ist nicht schneller. Dies führt häufig zu einem Cache-Fehler.
Jeffrey Drake
19
Cache vermissen? Ich bin mir nicht sicher wie. Vielleicht meinten Sie Branchenfehlvorhersage?
Catskul
2
Es scheint mir, dass dies zu einer Warnung vor verwirrenden Ganzzahl- und Booleschen Typen führen wird!
Sergiol
Wie schnell wird das mit der Branche gehen?
Nick
29

Gibt es eine Standardzeichenfunktion (signum, sgn) in C / C ++?

Ja, je nach Definition.

C99 und höher hat das signbit()Makro in<math.h>

int signbit(Real-Floating x);
Das signbitMakro gibt genau dann einen Wert ungleich Null zurück, wenn das Vorzeichen seines Argumentwerts negativ ist. C11 §7.12.3.6


Dennoch möchte OP etwas anderes.

Ich möchte eine Funktion, die -1 für negative Zahlen und +1 für positive Zahlen zurückgibt. ... eine Funktion, die an Schwimmern arbeitet.

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

Tiefer:

Die Stelle ist in folgenden Fällen nicht spezifisch : x = 0.0, -0.0, +NaN, -NaN.

Ein Klassiker signum()kehrt +1auf x>0, -1an x<0und 0auf x==0.

Viele Antworten haben dies bereits behandelt, aber nicht angesprochen x = -0.0, +NaN, -NaN. Viele sind auf eine ganzzahlige Sichtweise ausgerichtet, der normalerweise Not-a-Numbers ( NaN ) und -0,0 fehlen .

Typische Antworten funktionieren wie signnum_typical() Ein -0.0, +NaN, -NaN, sie kehren zurück 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;
}

Stattdessen schlage ich diese Funktionalität vor: Ein -0.0, +NaN, -NaN, es kehrt zurück -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 - Monica wieder einsetzen
quelle
1
Ah, genau das, wonach ich suche. Dies hat sich gerade in Pharo Smalltalk geändert. Github.com/pharo-project/pharo/pull/1835 und ich habe mich gefragt, ob es eine Art Standard (IEC 60559 oder ISO 10967) gibt, der das Verhalten für negatives Null- und Nanoverhalten vorschreibt ... Ich mag das Javascript sign developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
aka.nice
16

Es gibt eine Möglichkeit, dies ohne Verzweigung zu tun, aber es ist nicht sehr hübsch.

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

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

Viele andere interessante, übermäßig clevere Sachen auf dieser Seite ...

Tim Sylvester
quelle
1
Wenn ich den Link richtig gelesen habe, wird nur -1 oder 0 zurückgegeben. Wenn Sie -1, 0 oder +1 möchten, ist es sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));oder sign = (v > 0) - (v < 0);.
Z Boson
1
Dies impliziert, dass ves sich um einen ganzzahligen Typ handelt, der nicht breiter als int
phuclv ist.
12

Wenn Sie nur das Vorzeichen testen möchten, verwenden Sie signbit (gibt true zurück, wenn das Argument ein negatives Vorzeichen hat). Ich bin mir nicht sicher, warum Sie besonders -1 oder +1 zurückgeben möchten. Copysign ist dafür bequemer, aber es klingt so, als würde es auf einigen Plattformen +1 für negative Null zurückgeben, wobei negative Null nur teilweise unterstützt wird, wobei signbit vermutlich true zurückgeben würde.

ysth
quelle
7
Es gibt viele mathematische Anwendungen, in denen das Vorzeichen (x) erforderlich ist. Sonst würde ich es einfach tun if (x < 0).
Chance
5

Im Allgemeinen gibt es in C / C ++ keine Standard-Signum-Funktion, und das Fehlen einer solchen Grundfunktion sagt viel über diese Sprachen aus.

Abgesehen davon glaube ich, dass beide Ansichten der Mehrheit über den richtigen Ansatz zur Definition einer solchen Funktion in gewisser Weise richtig sind, und die "Kontroverse" darüber ist eigentlich kein Argument, wenn man zwei wichtige Vorbehalte berücksichtigt:

  • Eine Signum- Funktion sollte ähnlich wie eine abs()Funktion immer den Typ ihres Operanden zurückgeben, da Signum normalerweise zur Multiplikation mit einem Absolutwert verwendet wird, nachdem dieser irgendwie verarbeitet wurde. Daher ist der Hauptanwendungsfall von Signum nicht Vergleiche, sondern Arithmetik, und letztere sollte keine teuren Ganzzahl-zu-Von-Gleitkomma-Konvertierungen beinhalten.

  • Gleitkommatypen haben keinen einzigen exakten Nullwert: +0,0 kann als "infinitesimal über Null" und -0,0 als "infinitesimal unter Null" interpretiert werden. Aus diesem Grund müssen Vergleiche mit Null intern mit beiden Werten verglichen werden, und ein Ausdruck wie x == 0.0kann gefährlich sein.

In Bezug auf C denke ich, dass der beste Weg für integrale Typen in der Tat darin besteht, den (x > 0) - (x < 0)Ausdruck zu verwenden, da er verzweigungsfrei übersetzt werden sollte und nur drei grundlegende Operationen erfordert. Definieren Sie am besten Inline-Funktionen, die einen Rückgabetyp erzwingen, der dem Argumenttyp entspricht, und fügen Sie einen C11 hinzu define _Generic, um diese Funktionen einem allgemeinen Namen zuzuordnen.

Mit Gleitkommazahlen, ich denke , Inline - Funktionen basierend auf C11 copysignf(1.0f, x), copysign(1.0, x)und copysignl(1.0l, x)bin der Weg zu gehen, einfach weil sie sind auch sehr wahrscheinlich zu sein , astfrei und zusätzlich erfordert nicht das Ergebnis von integer zurück in eine Floating - Point - Casting Wert. Sie sollten wahrscheinlich deutlich darauf hinweisen, dass Ihre Gleitkommaimplementierungen von signum aufgrund der Besonderheiten von Gleitkomma-Nullwerten, Überlegungen zur Verarbeitungszeit und auch, weil es in der Gleitkomma-Arithmetik oft sehr nützlich ist, das richtige -1 / + zu erhalten, nicht Null zurückgeben 1 Vorzeichen, auch für Nullwerte.

Tabernakel
quelle
5

Meine Kopie von C auf den Punkt gebracht zeigt die Existenz einer Standardfunktion namens Copysign, die nützlich sein könnte. Es sieht so aus, als würde Copysign (1.0, -2.0) -1,0 und Copysign (1.0, 2.0) +1.0 zurückgeben.

Ziemlich nah, oder?

Hochleistungsmarke
quelle
Nicht Standard, aber möglicherweise weit verbreitet. Microsoft beginnt mit einem Unterstrich. Dies ist die Konvention, die für nicht standardmäßige Erweiterungen verwendet wird. Nicht die beste Wahl, wenn Sie mit ganzen Zahlen arbeiten.
Mark Ransom
5
Copysign ist sowohl in den Standards ISO C (C99) als auch POSIX enthalten. Siehe opengroup.org/onlinepubs/000095399/functions/copysign.html
lhf
3
Was ich gesagt habe. Visual Studio ist keine Referenz für den C-Standard.
Stephen Canon
3

Nein, es existiert nicht in c ++, wie in matlab. Ich benutze dafür ein Makro in meinen Programmen.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
Geschwätz
quelle
5
In C ++ sollte man Vorlagen Makros vorziehen.
Ruslan
In C gibt es keine Vorlage ...... helloacm.com/how-to-implement-the-sgn-function-in-c
doctorlai
Ich dachte, das wäre eine gute Antwort, dann schaute ich mir meinen eigenen Code an und fand Folgendes: Das #define sign(x) (((x) > 0) - ((x) < 0))ist auch gut.
Michel Rouzic
1
Eine Inline-Funktion ist besser als ein Makro in C, und in C ++ ist die Vorlage besser
phuclv
3

Die akzeptierte Antwort mit der folgenden Überladung löst in der Tat keine -Wtype-Limits aus .

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>());
}

Für C ++ 11 könnte eine Alternative sein.

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));  
}

Für mich löst es keine Warnungen auf GCC 5.3.1 aus.

SamVanDonut
quelle
Um die -Wunused-parameterWarnung zu vermeiden, verwenden Sie einfach unbenannte Parameter.
Jonathan Wakely
Das ist eigentlich sehr wahr. Das habe ich vermisst. Allerdings mag ich die C ++ 11-Alternative in beiden Fällen mehr.
SamVanDonut
2

Etwas abseits des Themas, aber ich benutze dies:

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));
}

und ich fand die erste Funktion - die mit zwei Argumenten - viel nützlicher als "standard" sgn (), da sie in Code wie diesem am häufigsten verwendet wird:

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

vs.

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

Es gibt keine Besetzung für vorzeichenlose Typen und kein zusätzliches Minus.

Tatsächlich habe ich diesen Code mit 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);
}
Nick
quelle
1

Die Frage ist alt, aber es gibt jetzt diese Art von gewünschter Funktion. Ich habe einen Wrapper mit not, left shift und dec hinzugefügt.

Sie können eine Wrapper-Funktion verwenden, die auf dem Signbit von C99 basiert , um das genau gewünschte Verhalten zu erhalten (siehe Code weiter unten).

Gibt zurück, ob das Vorzeichen von x negativ ist.
Dies kann auch auf Unendlichkeiten, NaNs und Nullen angewendet werden (wenn Null ohne Vorzeichen ist, wird dies als positiv betrachtet

#include <math.h>

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

NB: Ich verwende den Operanden nicht ("!"), Da der Rückgabewert von signbit nicht als 1 angegeben ist (obwohl die Beispiele uns glauben lassen, dass dies immer so wäre), sondern für eine negative Zahl gilt:

Rückgabewert
Ein Wert ungleich Null (true), wenn das Vorzeichen von x negativ ist. und sonst Null (falsch).

Dann multipliziere ich mit zwei mit der Linksverschiebung ("<< 1"), was 2 für eine positive Zahl und 0 für eine negative Zahl ergibt und schließlich um 1 dekrementiert, um 1 und -1 für jeweils positive und negative Zahlen zu erhalten, wie von angefordert OP.

Antonin GAVREL
quelle
0 wird dann auch positiv sein ... was vielleicht sein könnte oder nicht, was OP wollte ...
Antti Haapala
Nun, wir werden vielleicht nie wissen, was OP wirklich wollte, wenn n = 0 ...!
Antonin GAVREL
0

Obwohl die ganzzahlige Lösung in der akzeptierten Antwort ziemlich elegant ist, hat es mich gestört, dass sie NAN für doppelte Typen nicht zurückgeben kann, deshalb habe ich sie leicht modifiziert.

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

Beachten Sie, dass die Rückgabe eines Gleitkomma-NAN im Gegensatz zu einem fest codierten NAN NANin einigen Implementierungen dazu führt , dass das Vorzeichenbit gesetzt wird , sodass die Ausgabe für val = -NANund val = NANunabhängig davon identisch ist (wenn Sie eine " nan" Ausgabe gegenüber einer Ausgabe bevorzugen, die -nanSie setzen können) ein abs(val)vor der Rückkehr ...)

mrclng
quelle
0

Sie können die boost::math::sign()Methode verwenden, boost/math/special_functions/sign.hppwenn Boost verfügbar ist.

Khkarens
quelle
Beachten Sie, dass dies zuvor vorgeschlagen wurde: stackoverflow.com/a/16869019/1187415 .
Martin R
0

Hier ist eine verzweigungsfreundliche Implementierung:

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

Sofern Ihre Daten keine Nullen als die Hälfte der Zahlen haben, wählt der Zweigprädiktor hier einen der Zweige als den häufigsten aus. Beide Zweige beinhalten nur einfache Operationen.

Alternativ kann auf einigen Compilern und CPU-Architekturen eine vollständig verzweigungslose Version schneller sein:

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

Dies funktioniert für das binäre Gleitkommaformat IEEE 754 mit doppelter Genauigkeit: binary64 .

Serge Rogatch
quelle
-1
int sign(float n)
{     
  union { float f; std::uint32_t i; } u { n };
  return 1 - ((u.i >> 31) << 1);
}

Diese Funktion setzt voraus:

  • binäre 32- Darstellung von Gleitkommazahlen
  • Ein Compiler, der bei Verwendung einer benannten Union eine Ausnahme von der strengen Aliasing- Regel macht
Gigi
quelle
3
Hier gibt es immer noch einige schlechte Annahmen. Zum Beispiel glaube ich nicht, dass die Endianness des Floats garantiert die Endianness der ganzen Zahl ist. Ihre Prüfung schlägt auch bei Architekturen fehl, die ILP64 verwenden. Wirklich, Sie implementieren nur neu copysign; Wenn Sie verwenden, haben static_assertSie C ++ 11 und können es auch wirklich verwenden copysign.
-3
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
Cyberion
quelle
-3

Warum ternäre Operatoren verwenden und wenn-sonst, wenn Sie dies einfach tun können

#define sgn(x) x==0 ? 0 : x/abs(x)
Jagreet
quelle
3
Ihre Definition verwendet ebenfalls einen ternären Operator.
Martin R
Ja, definitiv, aber es wird nur ein ternärer Operator verwendet, um Null- und Nicht-Null-Zahlen zu trennen. Andere Versionen enthalten verschachtelte ternäre Operationen, um positive, negative und Null zu trennen.
Jagreet
Die Verwendung einer Ganzzahldivision ist sehr ineffizient und abs () gilt nur für Ganzzahlen.
Michel Rouzic
Undefiniertes Verhalten möglich, wenn x == INT_MIN.
chux - Monica