it-swarm.com.de

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

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 eigene zu schreiben, aber es scheint so, als ob es sich irgendwo in einer Standardbibliothek befinden sollte.

Edit: Konkret habe ich nach einer Funktion gesucht, die an Floats arbeitet.

377
batty

Überrascht hat noch niemand die typsichere C++ Version gepostet:

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 nicht signum ist. Außerdem geben einige Implementierungen hier eher ein float (oder T) als ein int zurück, was verschwenderisch erscheint.
  • Funktioniert für Ints, Floats, Doubles, Shorts ohne Vorzeichen oder alle benutzerdefinierten Typen, die aus der Ganzzahl 0 konstruiert und bestellt werden können.
  • Schnell! copysign ist langsam, besonders wenn Sie fördern und dann wieder verengen müssen. Dies ist verzweigungslos und optimiert hervorragend
  • Normenkonform! Der Bit-Shift-Hack ist ordentlich, funktioniert aber nur für einige Bit-Darstellungen und funktioniert nicht, wenn Sie einen nicht signierten Typ haben. Es kann gegebenenfalls als manuelle Spezialisierung vorgesehen werden.
  • Genau! Durch einfache Vergleiche mit Null kann die interne hochpräzise Darstellung der Maschine (z. B. 80 Bit bei x87) beibehalten und eine vorzeitige Rundung auf Null vermieden werden.

Vorsichtsmaßnahmen:

  • Da es sich um eine Vorlage handelt, kann das Kompilieren unter bestimmten Umständen länger dauern.
  • Anscheinend denken einige Leute, dass die Verwendung einer neuen, etwas esoterischen und sehr langsamen Standardbibliotheksfunktion die nicht einmal wirklich signum implementiert verständlicher ist.
  • Der < 0 -Teil der Prüfung löst die -Wtype-limits -Warnung des GCC aus, wenn er für einen nicht signierten Typ 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>());
    }
    

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

474
user79758

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

(x > 0) - (x < 0)

Hier ist eine besser lesbare Methode:

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)
261
Mark Byers

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

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

je nach Vorzeichen erhalten Sie ein Ergebnis von +/- 1,0. Beachten Sie, dass Gleitkomma-Nullen vorzeichenbehaftet sind: (+0) ergibt +1 und (-0) -1.

183
comingstorm

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

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

Nicht in der Standardbibliothek enthalten ist jedoch copysign , das über copysign(1.0, arg) fast genauso verwendet werden kann, und in boost gibt es eine echte Vorzeichenfunktion als auch ein Teil des Standards sein.

    #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

74
Catskul

Die Antwort auf die Frage des ursprünglichen Posters lautet offenbar nein. Es gibt keine Standard C++ sgn -Funktion.

74
John

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

(x < 0) ? -1 : (x > 0)
28
xnx

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

Ja, je nach Definition.

C99 und höher enthält das Makro signbit() in <math.h>

int signbit (echt schwebend x);
Das Makro signbit gibt nur dann einen Wert ungleich Null zurück, wenn das Vorzeichen seines Argumentwerts negativ ist. C11 §7.12.3.6


Doch OP will 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:

Der Beitrag ist in den folgenden Fällen nicht spezifisch, x = 0.0, -0.0, +NaN, -NaN.

Ein Klassiker signum() gibt +1 bei x>0, -1 bei x>0 und 0 bei x==0 zurück.

Viele Antworten haben dies bereits behandelt, sprechen jedoch nicht x = -0.0, +NaN, -NaN an. Viele sind auf eine ganzzahlige Sichtweise ausgerichtet, bei der es normalerweise an Not-a-Numbers ( NaN ) und - 0. mangelt.

Typische Antworten funktionieren wie signnum_typical(). Bei -0.0, +NaN, -NaN geben sie 0.0, 0.0, 0.0 zurück.

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

Schlagen Sie stattdessen diese Funktionalität vor: Bei -0.0, +NaN, -NaN wird -0.0, +NaN, -NaN zurückgegeben.

double signnum_c(double x) {
  if (x > 0.0) return 1.0;
  if (x < 0.0) return -1.0;
  return x;
}
23
chux

Es gibt eine Möglichkeit, es 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

Auf dieser Seite gibt es noch viele andere interessante, überaus clevere Dinge ...

16
Tim Sylvester

Wenn Sie nur das Vorzeichen testen möchten, verwenden Sie Vorzeichenbit (gibt true zurück, wenn das Argument ein negatives Vorzeichen hat). Nicht sicher, warum Sie besonders -1 oder +1 zurückgeben möchten; copysign ist dafür praktischer, aber es hört sich so an, als würde es +1 für negative Null auf einigen Plattformen mit nur teilweiser Unterstützung für negative Null zurückgeben, wobei das Vorzeichen vermutlich true zurückgeben würde.

11
ysth

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

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

  • Eine signum -Funktion sollte immer den Typ ihres Operanden zurückgeben, ähnlich einer abs() -Funktion, da signum normalerweise zur Multiplikation mit einem Absolutwert nach dem letzteren verwendet wird wurde irgendwie verarbeitet. Daher ist der Hauptanwendungsfall von signum kein Vergleich, sondern eine Arithmetik, und letztere sollte keine teuren Umwandlungen von Ganzzahlen in/von Gleitkomma beinhalten.

  • Gleitkommatypen weisen keinen einzigen exakten Nullwert auf: +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 abgeglichen werden, und ein Ausdruck wie x == 0.0 kann gefährlich sein.

In Bezug auf C denke ich, dass der beste Weg für Integraltypen in der Tat darin besteht, den Ausdruck (x > 0) - (x < 0) 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 C11 define _Generic hinzu, um diese Funktionen einem allgemeinen Namen zuzuordnen.

Ich denke, dass Inline-Funktionen mit Gleitkommawerten, die auf C11 copysignf(1.0f, x), copysign(1.0, x) und copysignl(1.0l, x) basieren, der richtige Weg sind, einfach weil sie höchstwahrscheinlich auch verzweigungsfrei sind. Außerdem müssen Sie das Ergebnis einer Ganzzahl nicht zurück in einen Gleitkommawert umwandeln. Sie sollten wahrscheinlich deutlich darauf hinweisen, dass Ihre Gleitkommaimplementierungen von signum aufgrund der Besonderheiten von Gleitkomma-Nullwerten, der Überlegungen zur Verarbeitungszeit und auch, weil dies häufig bei Gleitkomma-Arithmetik zu sehr nützlich ist, keine Null zurückgeben Erhalten Sie das richtige -1/+ 1-Zeichen, auch für Null-Werte.

5
Tabernakel

Meine Kopie von C in Kürze enthüllt 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.

Ganz in der Nähe, oder?

Die akzeptierte Antwort mit der unten stehenden Überlastung löst in der Tat nicht - 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 x) {
    return T(0) < x;  
}

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

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

3
SamVanDonut

Nein, es gibt es nicht in C++, wie in Matlab. Ich benutze dazu ein Makro in meinen Programmen.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
3
chattering

Ein bisschen off-topic, 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 erste Funktion - die mit zwei Argumenten, um von "Standard" sgn () viel nützlicher zu sein, weil es 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 nicht signierte Typen und kein zusätzliches Minus.

in der Tat habe ich dieses Stück 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);
}
1
Nick

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 enthalten, wählt der Verzweigungsprädiktor eine der Verzweigungen als die häufigste. Bei beiden Zweigen handelt es sich nur um 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 IEEE 754, binäres Gleitkommaformat mit doppelter Genauigkeit: binary64 .

0
Serge Rogatch

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

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

Beachten Sie, dass das Zurückgeben eines Gleitkomma-NAN im Gegensatz zu einem fest codierten NAN dazu führt, dass das Vorzeichenbit in einigen Implementierungen gesetzt wird , also die Ausgabe für val = -NAN und val = NAN werden identisch sein, egal was passiert (wenn Sie einen "nan" -Ausgang einem -nan vorziehen, können Sie einen abs(val) voranstellen die Rückkehr...)

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

Diese Funktion setzt voraus:

  • binary32 Darstellung von Gleitkommazahlen
  • ein Compiler, der bei Verwendung einer named Union eine Ausnahme zum strengen Aliasing -Regel erzeugt
0
Gigi

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

0
khkarens