Y a-t-il une fonction de signe standard (signum, sgn) en C/C++?

je veux une fonction qui retourne -1 pour les nombres négatifs et +1 pour les nombres positifs. http://en.wikipedia.org/wiki/Sign_function c/" class="blnk">C'est assez facile d'écrire le mien, mais ça ressemble à quelque chose qui devrait être dans une bibliothèque standard quelque part.

Edit: plus précisément, je cherchais une fonction travaillant sur flotteurs.

340
demandé sur batty 2009-12-15 01:27:15

23 réponses

surpris que personne n'ait encore posté la version C++ sans branchement et sans danger:

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

prestations:

  • implémente effectivement signum (-1, 0, ou 1). Les implémentations utilisant ici copysign ne renvoient que -1 ou 1, ce qui n'est pas signum. En outre, certaines implémentations ici renvoient un flottant (ou T) plutôt qu'un int, ce qui semble un gaspillage.
  • fonctionne pour ints, flotteurs, doubles, shorts non signés, ou tout type personnalisé constructible à partir de entier 0 et peut être commandé.
  • vite! copysign est lent, surtout si vous avez besoin de promouvoir et puis affinez à nouveau. C'est sans branchement et optimise excellemment
  • conforme aux normes! Le hack bitshift est soigné, mais ne fonctionne que pour quelques représentations de bits, et ne fonctionne pas quand vous avez un type non signé. Elle pourrait être fournie à titre de spécialisation manuelle, le cas échéant.
  • exact! Des comparaisons simples avec zéro peut maintenir la représentation interne de haute précision de la machine (par exemple 80 bits sur x87), et éviter un rond prématuré à zéro.

mises en garde:

  • C'est un modèle donc ça prendra une éternité à compiler.
  • apparemment, certaines personnes pensent que l'utilisation d'une nouvelle, quelque peu ésotérique, et très lente fonction de bibliothèque standard qui ne met même pas vraiment en œuvre signum est plus compréhensible.
  • la partie < 0 du contrôle déclenche L'avertissement -Wtype-limits de GCC lorsqu'elle est instanciée pour un type non signé. Vous pouvez éviter cela en utilisant quelques surcharges:

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

    (Qui est un bon exemple de la première mise en garde.)

435
répondu 2012-03-28 13:58:41

Je ne connais pas de fonction standard pour cela. Voici une façon intéressante de l'écrire:

(x > 0) - (x < 0)

Voici une façon plus lisible de le faire:

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

si vous aimez l'opérateur ternary, vous pouvez le faire:

(x > 0) ? 1 : ((x < 0) ? -1 : 0)
248
répondu Mark Byers 2009-12-15 08:19:52

il existe une fonction de bibliothèque de mathématiques C99 appelée copysign (), qui prend le signe d'un argument et la valeur absolue de l'autre:

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

vous donnera un résultat de +/- 1.0, en fonction du signe de valeur. Notez que les Zéros à virgule flottante sont signés: (+0) donnera +1, et (-0) donnera -1.

168
répondu comingstorm 2011-10-04 01:38:07

apparemment, la réponse à la question de l'affiche originale est non. Il n'y a pas de fonction standard C++ sgn .

67
répondu John 2012-04-13 00:24:37

il semble que la plupart des réponses n'aient pas répondu à la question initiale.

y a-t-il une fonction de signal standard (signum, sgn) en C/C++?

pas dans la bibliothèque standard, mais il y a en boost , qui pourrait aussi bien faire partie de la norme.

    #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

57
répondu Catskul 2013-06-01 04:23:45

plus rapide que les solutions ci-dessus, y compris la solution la mieux notée:

(x < 0) ? -1 : (x > 0)
28
répondu xnx 2012-10-11 00:02:10

il y a un moyen de le faire sans ramification, mais ce n'est pas très joli.

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

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

beaucoup d'autres choses intéressantes, trop intelligentes sur cette page, aussi...

16
répondu Tim Sylvester 2009-12-14 22:52:30

y a-t-il une fonction de signe standard (signum, sgn) en C/C++?

Oui, selon la définition.

C99 et plus tard a la signbit() macro <math.h>

int signbit (réel-flottant x );

La macro signbit renvoie une valeur non nulle si et seulement si le signe de sa valeur d'argument est négatif. C11 §7.12.3.6


pourtant OP veut quelque chose d'un peu différent.

je veux une fonction qui retourne -1 pour les nombres négatifs et +1 pour les nombres positifs. ... une fonction travaillant sur flotteurs.

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

plus profond:

Le poste n'est pas spécifique dans les cas suivants, x = 0.0, -0.0, +NaN, -NaN .

Un classique signum() renvoie +1 sur x>0 , -1 sur x>0 et 0 sur x==0 .

beaucoup de réponses ont déjà couvert cela, mais n'abordent pas x = -0.0, +NaN, -NaN . Beaucoup sont conçus pour un point de vue entier qui manque généralement pas-a-Nombres ( NaN ) et -0.0 .

réponses typiques fonction comme signnum_typical() sur -0.0, +NaN, -NaN , ils renvoient 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;
}

au lieu de cela, proposer cette fonctionnalité: sur -0.0, +NaN, -NaN , il retourne -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;
}
13
répondu chux 2016-02-18 20:24:08

si tout ce que vous voulez est de tester le signe, utilisez signbit (renvoie true si son argument a un signe négatif). Je ne sais pas pourquoi vous voulez que -1 ou +1 soit retourné; copysign est plus pratique pour cela, mais il semble qu'il retournera +1 pour zéro négatif sur certaines plates-formes avec seulement un support partiel pour le zéro négatif, où signbit retournerait probablement vrai.

10
répondu ysth 2010-01-04 03:16:31

en général, il n'y a pas de fonction signum standard en C/C++, et l'absence d'une telle fonction fondamentale en dit long sur ces langues.

en dehors de cela, je crois que les deux points de vue de la majorité sur la bonne approche pour définir une telle fonction sont en quelque sorte corrects, et la "controverse" à ce sujet est en fait un non-argument une fois que vous prenez en compte deux mises en garde importantes:

  • Un signum fonction doit toujours retourner le type de son opérande, similaire à une fonction abs() , parce que signe est habituellement utilisé pour la multiplication avec une valeur absolue après que cette dernière a été traitée d'une manière ou d'une autre. Par conséquent, le cas d'utilisation principale de signum n'est pas des comparaisons mais de l'arithmétique, et cette dernière ne devrait pas impliquer de coûteuses conversions entier-à/de-virgule flottante.

  • Les types à virgule flottante

    ne comportent pas une seule valeur exacte nulle: +0,0 peut être interprété comme" infinitésimalement au-dessus de zéro", et -0,0 comme"infinitésimalement au-dessous de zéro". C'est la raison pour laquelle les comparaisons impliquant zéro doivent être vérifiées à l'interne par rapport aux deux valeurs, et une expression comme x == 0.0 peut être dangereuse.

en ce qui concerne C, je pense que la meilleure façon d'aller de l'avant avec les types intégraux est en effet d'utiliser l'expression (x > 0) - (x < 0) , comme il se doit être traduit sans branche, et ne nécessite que trois opérations de base. Définissez au mieux les fonctions inline qui imposent un type de retour correspondant au type d'argument, et ajoutez un C11 define _Generic pour associer ces fonctions à un nom commun.

avec des valeurs à virgule flottante, je pense que les fonctions en ligne basées sur C11 copysignf(1.0f, x) , copysign(1.0, x) , et copysignl(1.0l, x) sont la voie à suivre, tout simplement parce qu'ils sont également très susceptibles d'être sans branche, et en outre ne nécessitent pas de casting le résultat d'entier de nouveau dans une valeur flottante de point. Vous devriez probablement commenter bien en évidence que vos implémentations de signum ne retourneront pas zéro en raison des particularités des valeurs zéro de la virgule flottante, des considérations de temps de traitement, et aussi parce qu'il est souvent très utile en arithmétique de la virgule flottante pour recevoir le signe correct -1/+1, même pour les valeurs zéro.

5
répondu Tabernakel 2015-05-26 13:07:43

ma copie de C en un mot révèle l'existence d'une fonction standard appelée copysign qui pourrait être utile. Il semble que copysign(1.0, -2.0) retournerait -1.0 et copysign (1.0, 2.0) retournerait +1.0.

pas loin, hein?

4
répondu High Performance Mark 2016-01-22 18:53:38

Non, il n'existe pas en c++, comme dans matlab. J'utilise une macro dans mes programmes pour cela.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
3
répondu chattering 2014-07-10 23:35:41

la réponse acceptée avec la surcharge ci-dessous ne déclenche en effet pas -wType-limits mais elle déclenche -wunused-parameter sur l'argument is_signed .

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

pour C++11 une alternative pourrait être.

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

pour moi, il ne déclenche pas d'avertissements sur GCC 5.3.1

3
répondu SamVanDonut 2017-01-13 17:14:26

un peu hors-sujet, mais j'utilise ceci:

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

et j'ai trouvé première fonction-celle avec deux arguments, pour être beaucoup plus utile de "standard" sgn (), parce qu'il est le plus souvent utilisé dans le code comme ceci:

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

vs.

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

il n'y a pas de fonte pour les types non signés et aucun moins supplémentaire.

en fait j'ai ce morceau de code en utilisant 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
répondu Nick 2017-07-15 20:03:53

Pourquoi utiliser des opérateurs ternaires et si-sinon quand vous pouvez simplement le faire

#define sgn(x) x==0 ? 0 : x/abs(x)
1
répondu Jagreet 2018-06-30 01:29:56
int sign(float n)
{     
  union { float f; std::uint32_t i; } u { n };
  return 1 - ((u.i >> 31) << 1);
}

cette fonction suppose:

  • binary32 la représentation des nombres en virgule flottante
  • un compilateur qui fait une exception à propos de la règle d'aliasing stricte en utilisant un nommé union
0
répondu Gigi 2012-08-14 19:12:43
#include<iostream>
#include<math.h>
using namespace std;
int main()
{
float k=10;
cout<<bool signbit(k); /* bool signbit(arg) will return "0" if arg passed is + 
                         else "1" */
return 0;
}

le code ci-dessus peut ne pas servir votre but (obtenir 1 ou -1), mais il rend certainement beaucoup plus facile de faire ressortir le signe du type de données (int, float, double etc)

0
répondu kuj 2017-07-22 15:41:16

bien que la solution de nombre entier dans la réponse acceptée est assez élégante, il m'a ennuyé qu'il ne serait pas en mesure de rendre NAN pour les types doubles, donc je l'ai modifié légèrement.

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

notez qu'en retournant un NaN flottant par opposition à un NAN codé en dur, le bit de signe est positionné dans quelques implémentations , de sorte que la sortie de val = -NAN et val = NAN va être identique quoi qu'il arrive (si vous préférez " nan sortie " sur un -nan vous pouvez mettre un abs(val) avant le retour...)

0
répondu mrclng 2017-08-11 08:19:11

vous pouvez utiliser boost::math::sign() méthode de boost/math/special_functions/sign.hpp si boost est disponible.

0
répondu khkarens 2018-08-29 07:58:19
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
-1
répondu cyberion 2012-11-04 16:05:06

j'ai rencontré ça aujourd'hui. Très bien, il n'y a pas de standard mais...

puisque L'OP avait juste besoin de amplifier la gamme de sortie et recentrer sur 0, (-1 à 1 pas 0 à 1) Pourquoi ne pas simplement le doubler et soustraire 1?

j'ai utilisé ceci:

(x<0)*2-1

Ou, en forçant un décalage de bits:

(x<0)< < 1-1

mais le le compilateur optimisera probablement cela de toute façon.

-1
répondu Alidor 2015-08-30 16:27:45

Ce sujet:

int sgn = x/fabs(x);

ça devrait bien marcher.

-2
répondu Daniel 2017-05-22 13:56:09

utiliser:

`#define sgn(x) (x<0)` 

par exemple:

`if(sng(n)) { etc ....}`

ou vous pouvez utiliser un code élaboré, mais casting en premier:

inline bool sgn_long(long x) { return ((x<0)? true: false); }

-4
répondu Roberto TUNINETTI 2017-11-15 22:25:28