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.
x==0
. Gemäß IEEE 754 sollten negative Null und positive Null als gleich verglichen werden.Antworten:
Überrascht hat noch niemand die typsichere C ++ - Version veröffentlicht:
Leistungen:
copysign
ist langsam, besonders wenn Sie fördern und dann wieder verengen müssen. Dies ist verzweigungslos und optimiert hervorragendVorsichtsmaßnahmen:
Der
< 0
Teil der Prüfung löst die-Wtype-limits
Warnung des GCC aus, wenn er für einen Typ ohne Vorzeichen instanziiert wird. Sie können dies vermeiden, indem Sie einige Überladungen verwenden:(Welches ist ein gutes Beispiel für die erste Einschränkung.)
quelle
std::copysign
scheint 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) ...copysign
ein 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.Ich kenne keine Standardfunktion dafür. Hier ist eine interessante Möglichkeit, es zu schreiben:
Hier ist eine besser lesbare Möglichkeit:
Wenn Sie den ternären Operator mögen, können Sie dies tun:
quelle
x==0
.<
,>
... soll 1 ergeben, wenn die angegebene Beziehung wahr ist, und 0, wenn sie falsch ist"0
ist "false"; jeder andere Wert ist "wahr"; Die relationalen und Gleichheitsoperatoren geben jedoch immer0
oder zurück1
(siehe Standard 6.5.8 und 6.5.9). - Der Wert des Ausdrucksa * (x == 42)
ist entweder0
odera
.copysign
für Integral verwenden,x
selbst wenn ich es zur Verfügung hätte.Es gibt eine C99-Mathematikbibliotheksfunktion namens copysign (), die das Vorzeichen von einem Argument und den Absolutwert von dem anderen übernimmt:
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.
quelle
Es scheint, dass die meisten Antworten die ursprüngliche Frage verfehlt haben.
Nicht in der Standardbibliothek, aber es gibt
copysign
eine, die fast auf die gleiche Weise über verwendet werden kann,copysign(1.0, arg)
und es gibt eine True-Sign-Funktion inboost
, die genauso gut Teil des Standards sein könnte.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
quelle
Anscheinend lautet die Antwort auf die Frage des Originalplakats nein. Es gibt keine Standard- C ++ -
sgn
Funktion.quelle
copysign()
macht Ihren ersten Parameter nicht 0.0, wenn der zweite 0.0 ist. Mit anderen Worten, John ist richtig.Schneller als die oben genannten Lösungen, einschließlich der am besten bewerteten:
quelle
Ja, je nach Definition.
C99 und höher hat das
signbit()
Makro in<math.h>
Dennoch möchte OP etwas anderes.
Tiefer:
Die Stelle ist in folgenden Fällen nicht spezifisch :
x = 0.0, -0.0, +NaN, -NaN
.Ein Klassiker
signum()
kehrt+1
aufx>0
,-1
anx<0
und0
aufx==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ück0.0, 0.0, 0.0
.Stattdessen schlage ich diese Funktionalität vor: Ein
-0.0, +NaN, -NaN
, es kehrt zurück-0.0, +NaN, -NaN
.quelle
Es gibt eine Möglichkeit, dies ohne Verzweigung zu tun, aber es ist nicht sehr hübsch.
http://graphics.stanford.edu/~seander/bithacks.html
Viele andere interessante, übermäßig clevere Sachen auf dieser Seite ...
quelle
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
odersign = (v > 0) - (v < 0);
.v
es sich um einen ganzzahligen Typ handelt, der nicht breiter als intWenn 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.
quelle
if (x < 0)
.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.0
kann 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 hinzudefine _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)
undcopysignl(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.quelle
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?
quelle
Nein, es existiert nicht in c ++, wie in matlab. Ich benutze dafür ein Makro in meinen Programmen.
quelle
#define sign(x) (((x) > 0) - ((x) < 0))
ist auch gut.Die akzeptierte Antwort mit der folgenden Überladung löst in der Tat keine -Wtype-Limits aus .
Für C ++ 11 könnte eine Alternative sein.
Für mich löst es keine Warnungen auf GCC 5.3.1 aus.
quelle
-Wunused-parameter
Warnung zu vermeiden, verwenden Sie einfach unbenannte Parameter.Etwas abseits des Themas, aber ich benutze dies:
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:
vs.
Es gibt keine Besetzung für vorzeichenlose Typen und kein zusätzliches Minus.
Tatsächlich habe ich diesen Code mit sgn ()
quelle
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).
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:
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.
quelle
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.
Beachten Sie, dass die Rückgabe eines Gleitkomma-NAN im Gegensatz zu einem fest codierten NAN
NAN
in einigen Implementierungen dazu führt , dass das Vorzeichenbit gesetzt wird , sodass die Ausgabe fürval = -NAN
undval = NAN
unabhängig davon identisch ist (wenn Sie eine "nan
" Ausgabe gegenüber einer Ausgabe bevorzugen, die-nan
Sie setzen können) einabs(val)
vor der Rückkehr ...)quelle
Sie können die
boost::math::sign()
Methode verwenden,boost/math/special_functions/sign.hpp
wenn Boost verfügbar ist.quelle
Hier ist eine verzweigungsfreundliche Implementierung:
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:
Dies funktioniert für das binäre Gleitkommaformat IEEE 754 mit doppelter Genauigkeit: binary64 .
quelle
Diese Funktion setzt voraus:
quelle
copysign
; Wenn Sie verwenden, habenstatic_assert
Sie C ++ 11 und können es auch wirklich verwendencopysign
.quelle
Warum ternäre Operatoren verwenden und wenn-sonst, wenn Sie dies einfach tun können
quelle
x == INT_MIN
.