Is er een standaard tekenfunctie (signum, sgn) in C/C++?

Ik wil een functie die -1 retourneert voor negatieve getallen en +1 voor positieve getallen.
http://en.wikipedia.org/wiki/Sign_function
Het is gemakkelijk genoeg om mijn eigen te schrijven, maar het lijkt iets dat ergens in een standaardbibliotheek zou moeten staan.

Bewerken: ik was specifiek op zoek naar een functie die aan floats werkt.


Antwoord 1, autoriteit 100%

Het verbaast me dat nog niemand de typeveilige C++-versie heeft gepost:

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

Voordelen:

  • Implementeert daadwerkelijk signum (-1, 0 of 1). Implementaties hier die copysign gebruiken, retourneren alleen -1 of 1, wat geen signum is. Ook retourneren sommige implementaties hier een float (of T) in plaats van een int, wat verspilling lijkt.
  • Werkt voor ints, floats, doubles, unsigned shorts of andere aangepaste typen die vanaf integer 0 kunnen worden gemaakt en besteld kunnen worden.
  • Snel! copysignis traag, vooral als je moet promoten en dan weer moet verkleinen. Dit is branchless en optimaliseert uitstekend
  • Voldoet aan de normen! De bitshift-hack is netjes, maar werkt alleen voor sommige bitrepresentaties en werkt niet als je een niet-ondertekend type hebt. Het kan indien nodig worden aangeboden als een handmatige specialisatie.
  • Nauwkeurig! Eenvoudige vergelijkingen met nul kunnen de interne, zeer nauwkeurige weergave van de machine behouden (bijv. 80 bit op x87) en een voortijdige afronding naar nul voorkomen.

Voorbehoud:

  • Het is een sjabloon, dus het kan in sommige omstandigheden langer duren om te compileren.
  • Blijkbaar denken sommige mensen het gebruik van een nieuwe, ietwat esoterische en zeer langzame standaard bibliotheekfunctie die niet echt echt Signum implementeert, is begrijpelijker.
  • De < 0deel van de cheque triggers GCC’s -Wtype-limitsWAARSCHUWING DIE INSTANTIONEERD VOOR EEN GESPESTELD TYPE. U kunt dit vermijden door enkele overbelasting te gebruiken:

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

    (wat een goed voorbeeld is van de eerste waarschuwing.)


Antwoord 2, Autoriteit 53%

Ik weet het niet van een standaardfunctie voor het. Hier is een interessante manier om het te schrijven:

(x > 0) - (x < 0)

Hier is een meer leesbare manier om het te doen:

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

Als u van de ternaire operator vindt, kunt u dit doen:

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

Antwoord 3, Autoriteit 38%

Er is een C95 Math-bibliotheekfunctie genaamd Copysign (), die het teken van het ene argument en de absolute waarde van de andere tonen:

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

geeft u een resultaat van +/- 1,0, afhankelijk van het teken van waarde. Merk op dat nullen met drijvende komma worden ondertekend: (+0) levert +1 op en (-0) levert -1 op.


Antwoord 4, autoriteit 16%

Het lijkt erop dat de meeste antwoorden de oorspronkelijke vraag hebben gemist.

Is er een standaard tekenfunctie (signum, sgn) in C/C++?

Niet in de standaardbibliotheek, maar er is copysigndie bijna op dezelfde manier kan worden gebruikt via copysign(1.0, arg)en er is een true sign-functie in boost, wat net zo goed onderdeel zou kunnen zijn van de standaard.

   #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


Antwoord 5, autoriteit 15%

Blijkbaar is het antwoord op de vraag van de oorspronkelijke poster nee. Er is geen standaardC++ sgn-functie.


Antwoord 6, autoriteit 6%

Is er een standaard tekenfunctie (signum, sgn) in C/C++?

Ja, afhankelijk van de definitie.

C99 en later heeft de macro signbit()in <math.h>

int signbit(real-floating x);
De signbit-macro retourneert een waarde die niet nul is als en alleen als het teken van zijn argumentwaarde negatief is. C11 §7.12.3.6


Toch wil OP iets anders.

Ik wil een functie die -1 retourneert voor negatieve getallen en +1 voor positieve getallen. … een functie die werkt op drijvers.

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

Dieper:

De vraag van OP is niet specifiek in de volgende gevallen: x = 0.0, -0.0, +NaN, -NaN.

Een klassieke signum()retourneert +1op x>0, -1op x<0en 0op x==0.

Veel antwoorden hebben dat al behandeld, maar gaan niet in op x = -0.0, +NaN, -NaN. Velen zijn gericht op een integer standpunt dat gewoonlijk geen Not-a-Numbers (NaN) en -0.0.

Typische antwoorden werken als signnum_typical()Op -0.0, +NaN, -NaNgeven ze 0.0, 0.0, 0.0terug.

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

In plaats daarvan stel ik deze functionaliteit voor: op -0.0, +NaN, -NaN, retourneert het -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;
}

Antwoord 7, Autoriteit 5%

sneller dan de bovenstaande oplossingen, inclusief de hoogste beoordeelde:

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

Antwoord 8, Autoriteit 3%

Er is een manier om het te doen zonder vertakt, maar het is niet erg mooi.

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

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

Veel andere interessante, overdreven slimme dingen op die pagina, ook …


Antwoord 9, Autoriteit 2%

Als alles wat u wilt is om het teken te testen, gebruikt u signbit (retourt waar als het argument een negatief teken heeft).
Weet je niet zeker waarom je vooral wilt – 1 of +1 geretourneerd; Copysign is handiger
daarvoor, maar het klinkt alsof het +1 zal terugkeren voor negatieve nul op sommige platforms met
Slechts gedeeltelijke ondersteuning voor negatieve nul, waar de bewegwijzering vermoedelijk true zou terugkeren.


Antwoord 10

In het algemeen is er geen standaard Signum-functie in C / C++, en het ontbreken van een dergelijke fundamentele functie vertelt u veel over deze talen.

Afgezien daarvan, geloof ik dat zowel meerderheidsviewpoints over de juiste benadering om een ​​dergelijke functie te definiëren op een manier correct zijn, en de “controverse” over het is eigenlijk een niet-argument als u rekening houdt met twee belangrijke voorbehouden:

  • A Signum -functie moet altijd het type operand retourneren, vergelijkbaar met een abs()-functie, omdat signum is Meestal gebruikt voor vermenigvuldiging met een absolute waarde nadat deze op de een of andere manier is verwerkt. Daarom is het hoofdgebruik van Signum niet vergelijkingen, maar rekenkundig, en de laatste zou geen dure gehele getal-tot / from-point-conversies moeten betrekken.

  • Drijvende puntentypen bevatten geen enkele exacte nulwaarde: +0.0 kan worden geïnterpreteerd als “oneindig boven nul”, en -0.0 als “Infinitesimaal onder nul”. Dat is de reden waarom vergelijkingen met nul die intern moeten controleren op beide waarden, als een uitdrukking zoals x == 0.0kan gevaarlijk zijn.

Betreffende C, ik denk dat de beste manier met ingevoerde typen inderdaad de (x > 0) - (x < 0)-xpressie gebruiken, zoals deze in een filiaal moet worden vertaald -Gratis mode en vereist slechts drie basisactiviteiten. Het beste definiëren inline-functies die een retourtype afdwingen dat overeenkomt met het argumenttype en voeg een C11 define _Genericom deze functies op een gemeenschappelijke naam in kaart te brengen.

Met zwevende puntwaarden denk ik inline-functies op basis van C11 copysignf(1.0f, x), copysign(1.0, x), EN copysignl(1.0l, x)zijn de manier om te gaan, simpelweg omdat ze ook zeer waarschijnlijk filiaal zijn, en bovendien het resultaat van gehele getal terug in een drijvende puntwaarde vereisen. U moet waarschijnlijk prominent commentaar geven dat uw drijvende puntimplementaties van Signum nul niet terugkeren vanwege de eigenaardigheden van zwevende punt nulwaarden, verwerkingstijdoverwegingen, en ook omdat het vaak erg nuttig is in drijvend punt rekenkundig Ontvang het juiste -1 / + 1-teken, zelfs voor nulwaarden.


Antwoord 11

Mijn exemplaar van C in een nutshell onthult het bestaan ​​van een standaardfunctie genaamd Copysign die nuttig kan zijn. Het lijkt erop dat Copysign (1.0, -2.0) -1.0 en COPYSIGN (1.0, 2.0) zou terugkeren +1.0.

vrij dichtbij huh?


Antwoord 12

Het geaccepteerde antwoord met de onderstaande overbelasting werkt inderdaad niet -wtype-limits .

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

Voor C++ 11 kan een alternatief zijn.

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

Voor mij activeert het geen waarschuwingen op GCC 5.3.1.


Antwoord 13

Nee, het bestaat niet in C++, zoals in Matlab. Ik gebruik hiervoor een macro in mijn programma’s.

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

Antwoord 14

De vraag is oud, maar er is nu dit soort gewenste functie. Ik heb een wikkel toegevoegd met niet, linkerverschuiving en dec.

U kunt een wrapper-functie gebruiken op basis van signbit van C99om precies het gewenste gedrag krijgen (zie code verderop).

Retourneert of het teken van x negatief is.
Dit kan ook worden toegepast op oneindig, NaN’s en nullen (als nul niet is ondertekend, wordt het als positief beschouwd

#include <math.h>
int signValue(float a) {
    return ((!signbit(a)) << 1) - 1;
}

NB: ik gebruik operand not (“!”) omdat de geretourneerde waarde van signbit niet is gespecificeerd als 1 (ook al laten de voorbeelden ons denken dat dit altijd zo zou zijn) maar waar voor een negatief getal:

p>

Retourwaarde
Een waarde die niet nul is (waar) als het teken van x negatief is; en anders nul (false).

Vervolgens vermenigvuldig ik met twee met left shift (” << 1″), wat ons 2 geeft voor een positief getal en 0 voor een negatief getal en tenslotte verlaag ik met 1 om 1 en -1 te krijgen voor respectievelijk positief en negatieve getallen zoals gevraagd door OP.


Antwoord 15

Beetje off-topic, maar ik gebruik dit:

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

en ik vond de eerste functie – die met twee argumenten, veel nuttiger van “standaard” sgn(), omdat deze het vaakst wordt gebruikt in code zoals deze:

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

vs.

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

er is geen cast voor niet-ondertekende typen en geen extra min.

in feite heb ik dit stukje code met 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);
}

Antwoord 16

Je kunt de boost::math::sign()methode van boost/math/special_functions/sign.hppgebruiken als boost beschikbaar is.


Antwoord 17

Hoewel de integer-oplossing in het geaccepteerde antwoord behoorlijk elegant is, stoorde het me dat het geen NAN zou kunnen retourneren voor dubbele typen, dus heb ik het enigszins aangepast.

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

Houd er rekening mee dat het retourneren van een zwevende-komma NAN in tegenstelling tot een hard gecodeerde nanervoor zorgt dat het tekenbit wordt ingesteld in sommige implementaties, dus de uitvoer voor val = -NANen val = NANzal identiek zijn, wat er ook gebeurt (als u de voorkeur geeft aan een “nan” output over een -nanje kunt een abs(val)plaatsen voor de return…)


Antwoord 18

Hier is een vertakkingsvriendelijke implementatie:

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

Tenzij uw gegevens nullen hebben als de helft van de getallen, kiest de vertakkingsvoorspeller hier een van de vertakkingen als de meest voorkomende. Beide takken omvatten slechts eenvoudige handelingen.

Als alternatief kan op sommige compilers en CPU-architecturen een volledig vertakkingsloze versie sneller zijn:

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

Dit werkt voor IEEE 754 double-precision binary drijvende-komma-indeling: binary64.


Antwoord 19

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

Deze functie veronderstelt:

  • binary32weergave van getallen met drijvende komma
  • een compiler die een uitzondering maakt voor de strikte aliasing-regel bij gebruik van een benoemdeunie

Antwoord 20

double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }

Antwoord 21

Waarom ternaire operatoren en als-anders gebruiken als dit ook gewoon kan

#define sgn(x) x==0 ? 0 : x/abs(x)

Other episodes