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

Lesezeit: 9 Minuten

Gibt es eine Standardzeichenfunktion signum sgn in CC
bekloppt

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 enthalten sein sollte.

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

  • Was soll es für 0 zurückgeben?

    – Craig McQueen

    14. Dezember 2009 um 23:26 Uhr

  • @Craig McQueen; das hängt davon ab, ob es sich um eine positive Null oder eine negative Null handelt.

    – ysth

    15. Dezember 2009 um 5:53 Uhr

  • @ysth @Craig McQueen, falsch auch für Floats, oder? sgn(x)s Definition sagt, 0 zurückzugeben, wenn x==0. Entsprechend IEEE754negative Null und positive Null sollten gleich sein.

    – RJFalconer

    4. Juni 2014 um 11:28 Uhr


  • @ysth “es hängt von positiver Null oder negativer Null ab”. In der Tat nicht.

    – RJFalconer

    6. Juni 2014 um 8:10 Uhr

  • Spät kommentiert, aber in Bezug auf vorzeichenbehaftete Nullen ist eine andere vernünftige Option, dass sgn(x) x zurückgibt, wenn x Null ist. Mit anderen Worten, Sie erhalten 0 heraus, aber es ist eine vorzeichenbehaftete Null mit demselben Vorzeichen wie die Eingabe. @RJFalconer In den relativ wenigen Fällen, in denen vorzeichenbehaftete Nullen eine Rolle spielen, erhalten Sie eine vernünftige Antwort, und in den anderen Fällen macht es keinen Unterschied.

    – Backstein

    15. Februar 2018 um 19:14 Uhr

Die typsichere C++-Version:

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

Leistungen:

  • Implementiert tatsächlich signum (-1, 0 oder 1). Implementierungen hier, die copysign verwenden, geben nur -1 oder 1 zurück, was nicht signum ist. Außerdem geben einige Implementierungen hier einen Float (oder T) anstelle eines Int zurück, was verschwenderisch erscheint.
  • Funktioniert für Ints, Floats, Doubles, unsigned Shorts oder alle benutzerdefinierten Typen, die aus Integer 0 konstruierbar und bestellbar sind.
  • Schnell! copysign ist langsam, besonders wenn Sie fördern und dann wieder verengen müssen. Diese ist verzweigt und optimiert hervorragend
  • Normgerecht! Der Bitshift-Hack ist ordentlich, funktioniert aber nur für einige Bitdarstellungen und funktioniert nicht, wenn Sie einen unsignierten Typ haben. Es könnte gegebenenfalls als manuelle Spezialisierung angeboten werden.
  • Genau! Einfache Vergleiche mit Null können die interne hochpräzise Darstellung der Maschine (z. B. 80 Bit auf x87) beibehalten und ein vorzeitiges Runden auf Null vermeiden.

Vorbehalte:

  • Da es sich um eine Vorlage handelt, kann die Kompilierung unter Umständen länger dauern.

  • Anscheinend denken einige Leute an die Verwendung einer neuen, etwas esoterischen und sehr langsamen Standardbibliotheksfunktion das implementiert signum nicht einmal wirklich ist verständlicher.

  • Die < 0 Ein Teil der Prüfung löst GCCs aus -Wtype-limits Warnung bei Instanziierung für einen unsignierten Typ. 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>());
     }
    

    (Was ein gutes Beispiel für den ersten Vorbehalt ist.)

  • @GMan: GCC hat erst jetzt (4.5) aufgehört, Kosten quadratisch zur Anzahl der Instanziierungen für Vorlagenfunktionen zu haben, und sie sind immer noch drastisch teurer zu analysieren und zu instanziieren 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, was dazu führt, dass die Abhängigkeitsberechnung länger dauert und kleine Änderungen (oft Implementierung, nicht Schnittstelle) erforderlich sind, um zu erzwingen, dass mehr Dateien neu kompiliert werden.

    Benutzer79758

    5. Januar 2011 um 22:42 Uhr

  • @Joe: Ja, und es gibt immer noch keine spürbaren Kosten. C++ verwendet Templates, das müssen wir alle verstehen, akzeptieren und darüber hinwegkommen.

    – GManNickG

    5. Januar 2011 um 22:54 Uhr

  • Warten Sie, was soll dieses „Copysign ist langsam“-Geschäft…? Mit aktuellen Compilern (g++ 4.6+, clang++ 3.0), std::copysign scheint zu resultieren Ausgezeichnet Code für mich: 4 Anweisungen (inlined), keine Verzweigung, vollständig mit der FPU. Das in dieser Antwort angegebene Rezept generiert im Gegensatz dazu viel schlechteren Code (viel mehr Anweisungen, einschließlich einer Multiplikation, die sich zwischen ganzzahliger Einheit und FPU hin und her bewegt) …

    – Snogglethorpe

    23. Januar 2012 um 6:35 Uhr


  • @snogglethorpe: Wenn du anrufst copysign bei einem int wird es zu Float/Double hochgestuft und muss bei der Rückkehr wieder schmaler werden. Ihr Compiler optimiert diese Promotion möglicherweise, aber ich kann nichts finden, was darauf hindeutet, dass dies vom Standard garantiert wird. Auch um signum über copysign zu implementieren, müssen Sie den 0-Fall manuell behandeln – bitte stellen Sie sicher, dass Sie dies in jeden Leistungsvergleich einbeziehen.

    Benutzer79758

    23. Januar 2012 um 9:31 Uhr


  • Die erste Version ist nicht astlos. Warum denken die Leute, dass ein in einem Ausdruck verwendeter Vergleich keine Verzweigung erzeugt? Es wird auf den meisten Architekturen funktionieren. Nur Prozessoren, die eine cmove (oder Prädikation) haben, generieren verzweigungslosen Code, aber sie tun dies auch für Ternäre oder wenn/sonst, wenn es ein Gewinn ist.

    – Patrick Schlüter

    12. März 2012 um 15:41 Uhr

Gibt es eine Standardzeichenfunktion signum sgn in CC
Markus Byers

Mir ist keine Standardfunktion dafür bekannt. Hier ist eine interessante Art, es zu schreiben:

(x > 0) - (x < 0)

Hier ist ein besser lesbarer Weg, dies zu tun:

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 Ransom, Ihre Ausdrücke geben falsche Ergebnisse für x==0.

    – Avakar

    14. Dezember 2009 um 22:45 Uhr

  • @Svante: „Jeder der Operatoren <, > … soll 1 ergeben, wenn die angegebene Relation wahr ist und 0, wenn sie falsch ist”

    – Stefan Kanon

    14. Dezember 2009 um 23:15 Uhr

  • @Svante: nicht genau. Ein Wert von 0 ist falsch”; jeder andere Wert ist “true”; Die Vergleichs- und Gleichheitsoperatoren kehren jedoch immer zurück 0 oder 1 (siehe Standard 6.5.8 und 6.5.9). — der Wert des Ausdrucks a * (x == 42) entweder 0 oder a.

    – pmg

    14. Dezember 2009 um 23:21 Uhr

  • High-Performance Mark, ich bin erstaunt, dass Sie das C++-Tag übersehen haben. Diese Antwort ist sehr gültig und verdient keine Ablehnung. Außerdem würde ich nicht verwenden copysign für integral x auch wenn ich es zur Verfügung hätte.

    – Avakar

    15. Dezember 2009 um 8:39 Uhr

  • Hat jemand tatsächlich überprüft, welchen Code GCC/G++/ein anderer Compiler auf einer echten Plattform ausgibt? Meine Vermutung ist, dass die “zweiglose” Version zwei Zweige anstelle von einem verwendet. Bitshifting ist wahrscheinlich viel schneller – und in Bezug auf die Leistung portabler.

    – Jörgen Fogh

    2. September 2011 um 11:29 Uhr

1647060613 359 Gibt es eine Standardzeichenfunktion signum sgn in CC
kommender Sturm

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

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

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

  • Diese Antwort wurde positiv und die beliebteste Antwort abgelehnt. Ich bin erstaunt, dass die SO-Community einen Hack der Verwendung einer Standardbibliotheksfunktion vorzuziehen scheint. Mögen die Götter der Programmierung Sie alle dazu verdammen, Hacks zu entschlüsseln, die von cleveren Programmierern verwendet werden, die mit Sprachstandards nicht vertraut sind. Ja, ich weiß, das wird mich eine Menge Repräsentanten bei SO kosten, aber ich halte lieber auf der Seite von Comingstorm als der Rest von euch …

    – Höchstleistungszeichen

    15. Dezember 2009 um 7:42 Uhr

  • Dies ist nah dran, gibt aber die falsche Antwort für Null (zumindest gemäß dem Wikipedia-Artikel in der Frage). Aber netter Vorschlag. +1 sowieso.

    – Mark Byers

    15. Dezember 2009 um 8:25 Uhr

  • 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 positive Antwort funktioniert auch und ist breiter anwendbar.

    – Pavel Minaev

    31. Dezember 2009 um 9:17 Uhr

  • würde ich nicht verwenden copysign() Auf einem AVR-Mikrocontroller fügt es der Programmgröße im Vergleich zu den “Hacks” erstaunliche 334 Bytes hinzu (wenn nicht bereits etwas anderes von verwendet wird math.h).

    – Torsten Römer

    29. Februar 2016 um 12:53 Uhr

  • Ich bin im Allgemeinen für die Verwendung von Standardbibliotheksfunktionen, aber dies tut wirklich nicht das, was genau wegen der Anmerkung am Ende über vorzeichenbehaftete Gleitkommazahlen 0 angefordert wurde. Wenn Ihr Anwendungsfall wirklich möchte, dass sgn(0) +1 oder – gibt 1, dann ist das in Ordnung, aber ich denke, dass die meisten Leute, die nach einer sgn-Funktion suchen, wollen, dass diese immer 0 ergibt, da dies die übliche mathematische Konvention ist und mit anderen Sprachen übereinstimmt.

    – Backstein

    14. März 2016 um 11:57 Uhr


1647060613 687 Gibt es eine Standardzeichenfunktion signum sgn in CC
Katzenkul

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 copysign die fast genauso verwendet werden kann via copysign(1.0, arg) und es gibt eine wahre Vorzeichenfunktion boostwas auch zum Standard gehören 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

Anscheinend lautet die Antwort auf die Frage des ursprünglichen Posters nein. Es gibt kein Standard C++ sgn Funktion.

  • @SR_ Sie haben nicht recht. copysign() wird Ihren ersten Parameter nicht zu 0.0 machen, wenn der zweite 0.0 ist. Mit anderen Worten, John hat recht.

    – Alexis Wilke

    16. Juni 2019 um 4:20 Uhr


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

Ja, je nach Definition.

C99 und höher hat die signbit() Makro ein <math.h>

int signbit(echt schwebend x);
Die signbit Das Makro gibt genau 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 mit Floats arbeitet.

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

Tiefer:

Die Frage von OP ist in den folgenden Fällen nicht spezifisch: x = 0.0, -0.0, +NaN, -NaN.

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

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

Typische Antworten funktionieren wie signnum_typical() An -0.0, +NaN, -NaNsie 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, -NaNes 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;
}

  • @SR_ Sie haben nicht recht. copysign() wird Ihren ersten Parameter nicht zu 0.0 machen, wenn der zweite 0.0 ist. Mit anderen Worten, John hat recht.

    – Alexis Wilke

    16. Juni 2019 um 4:20 Uhr


1647060614 397 Gibt es eine Standardzeichenfunktion signum sgn in CC
Tim Cooper

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

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

  • Welcher Typ ist x? Oder verwendest du ein #define?

    – Chance

    20. Februar 2012 um 18:11 Uhr

  • Ihr Typ ist nicht schneller. Es wird ziemlich oft einen Cache-Miss verursachen.

    – Jeffrey Drake

    30. Dezember 2012 um 3:19 Uhr

  • Cache-Verfehlung? Ich bin mir nicht sicher, wie. Vielleicht meinten Sie Verzweigungsfehlvorhersage?

    – Katskul

    1. Juni 2013 um 5:10 Uhr

  • Es scheint mir, dass dies zu einer Warnung vor verwirrenden Integer- und Boolean-Typen führen wird!

    – sergiol

    2. September 2015 um 11:55 Uhr

  • wie wird das schnell mit der abzweigung?

    – Nick

    15. Juli 2017 um 19:55 Uhr

992710cookie-checkGibt es eine Standardzeichenfunktion (signum, sgn) in C/C++?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy