519 Stimmen

Gibt es eine Standard-Vorzeichenfunktion (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 etwas zu sein, das irgendwo in einer Standardbibliothek sein sollte.

Edit: Ich habe speziell nach einer Funktion gesucht, die mit Floats arbeitet.

28 Stimmen

Was sollte er für 0 zurückgeben?

91 Stimmen

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

1 Stimmen

Mir ist aufgefallen, dass Sie den Rückgabewert als Ganzzahl angegeben haben. Suchen Sie nach einer Lösung, die Ganzzahlen oder Fließkommazahlen verarbeitet?

630voto

Die typsichere C++-Version:

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

Vorteile:

  • Implementiert eigentlich 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 eher einen Float (oder T) als einen Int zurück, was verschwenderisch erscheint.
  • Funktioniert für Ints, Floats, Doubles, Shorts ohne Vorzeichen oder beliebige benutzerdefinierte Typen, die aus Integer 0 konstruiert und geordnet werden können.
  • Schnell! copysign ist langwierig, vor allem, wenn man sie erst fördern und dann wieder einschränken muss. Dies ist verzweigungslos und optimiert hervorragend
  • Normenkonform! Der Bitshift-Hack ist nett, funktioniert aber nur für einige Bit-Darstellungen und funktioniert nicht, wenn Sie einen vorzeichenlosen Typ haben. Er könnte als manuelle Spezialisierung bereitgestellt werden, wenn es angebracht ist.
  • Treffsicher! Einfache Vergleiche mit Null können die interne Hochpräzisionsdarstellung des Rechners (z. B. 80 Bit bei 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.

  • Offenbar glauben einige Leute, dass die Verwendung einer neuen, etwas esoterischen und sehr langsamen Standardbibliotheksfunktion die nicht einmal wirklich signum implementiert ist besser verständlich.

  • El < 0 Teil der Prüfung löst den GCC aus -Wtype-limits Warnung bei der Instanziierung für einen Typ ohne Vorzeichen. 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>());
     }

    (Dies ist ein gutes Beispiel für die erste Warnung.)

4 Stimmen

"Es ist eine Vorlage, also wird es ewig dauern, sie zu kompilieren." Hm? Ich meine, technisch gesehen wird es gar nicht wirklich "kompiliert", sondern nur einmal durchlaufen. Wenn Sie es instanziieren, wird es kompiliert, aber nicht langsamer als ein manuell geschriebenes mit einem bestimmten T .

20 Stimmen

@GMan: GCC hat gerade erst (4.5) aufgehört, Kosten zu haben, die quadratisch zur Anzahl der Instanziierungen für Template-Funktionen sind, und sie sind immer noch drastisch teurer zu parsen 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. Templates ermutigen auch zu #includes-in-#includes, was dazu führt, dass die Berechnung von Abhängigkeiten länger dauert und kleine Änderungen (oft an der Implementierung, nicht an der Schnittstelle) dazu führen, dass mehr Dateien neu kompiliert werden müssen.

2 Stimmen

@Joe: Ich stimme Ihnen zu, aber ich denke, dass Sie die Kosten stark übertreiben; eine einzelne Funktionsvorlage hat sicherlich keine spürbaren Kosten.

327voto

Mark Byers Punkte 761508

Eine Standardfunktion dafür kenne ich nicht. Hier ist eine interessante Art, es zu schreiben:

(x > 0) - (x < 0)

Hier ist ein besser lesbarer Weg:

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)

4 Stimmen

Es wäre interessant, sie gegen x<0?-1:1 und 1-2*(x<0) zu messen.

9 Stimmen

Mark Ransom, Ihre Ausdrücke liefern falsche Ergebnisse für x==0 .

1 Stimmen

Ich würde das für den Monatspreis vorschlagen Stil-Auszeichnung Abzeichen, falls es eines gab.

254voto

comingstorm Punkte 24293

Es gibt eine Funktion der C99-Mathematikbibliothek namens copysign(), die das Vorzeichen aus einem Argument und den Absolutwert aus dem anderen übernimmt:

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

ergibt ein Ergebnis von +/- 1,0, je nach Vorzeichen des Wertes. Beachten Sie, dass Fließkomma-Nullen vorzeichenbehaftet sind: (+0) ergibt +1, und (-0) ergibt -1.

77 Stimmen

Diese Antwort hochgestuft, die beliebteste Antwort heruntergestuft. Ich bin fassungslos, dass die SO-Gemeinschaft einen Hack der Verwendung einer Standardbibliotheksfunktion vorzuziehen scheint. Mögen die Götter der Programmierung euch alle dazu verdammen, zu versuchen, Hacks zu entschlüsseln, die von cleveren Programmierern verwendet werden, die mit Sprachstandards nicht vertraut sind. Ja, ich weiß, dass mich das eine Menge Ansehen bei SO kosten wird, aber ich stelle mich lieber auf die Seite von Comingstorm als auf die von euch anderen ...

40 Stimmen

Das ist nahe dran, aber es gibt die falsche Antwort für Null (zumindest laut dem Wikipedia-Artikel in der Frage). Trotzdem ein guter Vorschlag. Trotzdem +1.

5 Stimmen

Wenn Sie eine ganze Zahl oder das exakte Signum-Ergebnis für Nullen wollen, gefällt mir Mark Byers' Antwort, die teuflisch elegant ist! Wenn Sie sich nicht um die oben genannten Punkte kümmern, könnte copysign() je nach Anwendung einen Leistungsvorteil haben - wenn ich eine kritische Schleife optimieren würde, würde ich beide ausprobieren.

102voto

Catskul Punkte 16549

Es scheint, dass die meisten Antworten an der ursprünglichen Frage vorbeigehen.

Gibt es eine Standard-Vorzeichenfunktion (signum, sgn) in C/C++?

Nicht in der Standardbibliothek, aber es gibt copysign die fast auf die gleiche Weise verwendet werden kann über copysign(1.0, arg) und es gibt eine echte Vorzeichenfunktion in boost die ebenso gut Teil der Norm sein könnten.

    #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

0 Stimmen

Ich habe mich in den letzten Minuten gefragt, warum die Standardbibliothek keine Vorzeichenfunktion hat. Sie ist einfach so gebräuchlich - auf jeden Fall gebräuchlicher als die Gamma-Funktion, die man im cmath-Header finden könnte.

7 Stimmen

Die Erklärung, die ich oft für ähnliche Fragen bekomme, lautet: "Es ist einfach genug, es selbst zu implementieren", was IMO kein guter Grund ist. Sie täuscht völlig über die Probleme der Standardisierung, der nicht offensichtlichen Randfälle und der Frage, wo ein so weit verbreitetes Tool untergebracht werden soll, hinweg.

0 Stimmen

Ich würde nicht erwarten, dass dies als Antwort markiert wird, weil es heißt, eine externe Nicht-Standard-Bibliothek zu verwenden. Ich benutze nicht Boost und kann nicht Boost verwenden, so dass dies nicht hilfreich ist.

99voto

John Punkte 5556

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

5 Stimmen

@SR_ Sie haben nicht recht. copysign() macht den ersten Parameter nicht 0,0, wenn der zweite 0,0 ist. Mit anderen Worten: John hat recht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X