468 votes

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

Je veux une fonction qui renvoie -1 pour les nombres négatifs et +1 pour les nombres positifs. http://en.wikipedia.org/wiki/Sign_function Il est assez facile d'écrire mon propre code, mais il semble que ce soit quelque chose qui devrait se trouver quelque part dans une bibliothèque standard.

Edit : Plus précisément, je cherchais une fonction fonctionnant sur les flottants.

21 votes

Que doit-il retourner pour 0 ?

78 votes

@Craig McQueen ; cela dépend si c'est un zéro positif ou un zéro négatif.

1 votes

J'ai remarqué que vous avez spécifié la valeur de retour comme un nombre entier. Cherchez-vous une solution qui prend des entiers ou des nombres à virgule flottante ?

5voto

Ma copie de C in a Nutshell révèle l'existence d'une fonction standard appelée copysign qui pourrait être utile. Il semble que copysign(1.0, -2.0) renvoie -1.0 et copysign(1.0, 2.0) renvoie +1.0.

Assez proche, hein ?

0 votes

Non standard, mais peut être largement disponible. Celle de Microsoft commence par un trait de soulignement, ce qui est la convention qu'ils utilisent pour les extensions non standard. Ce n'est cependant pas le meilleur choix lorsque vous travaillez avec des entiers.

5 votes

Copysign est à la fois dans les normes ISO C (C99) et POSIX. Voir opengroup.org/onlinepubs/000095399/functions/copysign.html

3 votes

Ce que lhf a dit. Visual Studio n'est pas une référence pour la norme C.

4voto

SamVanDonut Points 109

La réponse acceptée avec la surcharge ci-dessous ne déclenche en effet pas -Wtype-limites . Mais ça déclenche argument non utilisé les avertissements (sur le is_signed variable). Pour éviter cela, le deuxième argument ne doit pas être nommé de cette manière :

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

Pour C++11 et plus, une alternative pourrait être.

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

Pour moi, il ne déclenche aucun avertissement sous GCC 5.3.1.

0 votes

Pour éviter le -Wunused-parameter l'avertissement utilise simplement des paramètres sans nom.

0 votes

C'est en fait très vrai. Ça m'a échappé. Cependant, je préfère l'alternative C++11 dans tous les cas.

3voto

chattering Points 1

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

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

5 votes

Il faut préférer les modèles aux macros en C++.

0 votes

En C, il n'y a pas de modèle ...... helloacm.com/how-to-implementation-the-sgn-function-in-c

0 votes

Je pensais que c'était une bonne réponse, puis j'ai regardé mon propre code et j'ai trouvé ceci : #define sign(x) (((x) > 0) - ((x) < 0)) ce qui est bien aussi.

3voto

Antonin GAVREL Points 559

La question est ancienne mais il existe aujourd'hui ce type de fonction souhaitée. J'ai ajouté un wrapper avec not, left shift et dec.

Vous pouvez utiliser une fonction wrapper basée sur signbit de C99 afin d'obtenir le comportement exact souhaité (voir le code plus loin).

Retourne si le signe de x est négatif.
Cela peut également être appliqué aux infinis, aux NaN et aux zéros (si le zéro est non signé, il est considéré comme positif).

#include <math.h>

int signValue(float a) {
    return ((!signbit(a)) << 1) - 1;
}

NB : J'utilise l'opérande not (" !") parce que la valeur de retour de signbit n'est pas spécifiée pour être 1 (même si les exemples nous laissent penser que ce serait toujours comme ça) mais vraie pour un nombre négatif :

Valeur de retour
Une valeur non nulle (vrai) si le signe de x est négatif ; et zéro (faux) sinon.

Ensuite je multiplie par deux avec décalage à gauche (" << 1") ce qui nous donnera 2 pour un nombre positif et 0 pour un négatif et enfin je décrémente par 1 pour obtenir 1 et -1 pour respectivement les nombres positifs et négatifs comme demandé par OP.

1 votes

0 sera alors positif aussi... ce qui pourrait ou non être ce que le PO voulait...

0 votes

Eh bien, nous ne saurons peut-être jamais ce que le PO voulait vraiment si n=0... !

2voto

Nick Points 662

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é la première fonction - celle avec deux arguments - beaucoup plus utile que la sgn() "standard", car elle est le plus souvent utilisée dans un code comme celui-ci :

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 cast pour les types non signés et pas de moins supplémentaire.

en fait, j'ai ce morceau de code qui utilise 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);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X