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 Il est assez facile d'écrire le mien, mais cela semble être quelque chose qui devrait être dans une bibliothèque standard quelque part.
Edit: Plus précisément, je recherchais une fonction travaillant sur les flotteurs.
Surpris, personne n'a encore publié la version C++ sécurisée pour le type:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
Avantages:
copysign
est lent, surtout si vous devez promouvoir et resserrer à nouveau. Ceci est sans branche et optimise parfaitementMises en garde:
La partie < 0
de la vérification déclenche l'avertissement -Wtype-limits
de GCC lorsqu'elle est instanciée pour un type non signé. Vous pouvez éviter cela en utilisant des 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>());
}
(Ce qui est un bon exemple de la première mise en garde.)
Je ne connais pas de fonction standard pour cela. Voici un moyen intéressant de l'écrire:
(x > 0) - (x < 0)
Voici un moyen plus lisible de le faire:
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
Si vous aimez l'opérateur ternaire, vous pouvez le faire:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
Il existe une fonction de bibliothèque mathématique 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 la valeur. Notez que les zéros en virgule flottante sont signés: (+0) donnera +1 et (-0) donnera -1.
Il semble que la plupart des réponses ont manqué la question initiale.
Existe-t-il une fonction de signe standard (signum, sgn) en C/C++?
Pas dans la bibliothèque standard, cependant il y a copysign
qui peut être utilisé presque de la même manière via copysign(1.0, arg)
et il existe une vraie fonction de signe dans boost
, ce 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);
Apparemment, la réponse à la question de l'affiche originale est non. Il n'y a pas de fonction standard C++ sgn
.
Plus rapide que les solutions ci-dessus, y compris la solution la mieux notée:
(x < 0) ? -1 : (x > 0)
Existe-t-il une fonction de signe standard (signum, sgn) en C/C++?
Oui, selon la définition.
C99 et versions ultérieures ont la macro signbit()
dans <math.h>
int signbit
(variable réellex
);
La macrosignbit
renvoie une valeur différente de zéro 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 des flotteurs.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
Plus profond:
Le message 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 de Not-a-Numbers ( NaN ) et - 0. .
Les réponses typiques fonctionnent comme signnum_typical()
. Sur -0.0, +NaN, -NaN
, elles 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, proposez 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;
}
Il y a moyen de le faire sans créer de branche, 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 et trop intelligentes sur cette page aussi ...
Si tout ce que vous voulez, c'est tester le signe, utilisez signbit (retourne vrai si son argument a un signe négatif). Vous ne savez pas pourquoi vous voudriez particulièrement que -1 ou +1 soit retourné; copysign est plus pratique pour cela, mais il semblerait que cela renvoie +1 pour le zéro négatif sur certaines plates-formes ne prenant en charge que partiellement le zéro négatif, où signbit renverrait vraisemblablement.
En général, il n'y a pas de fonction de signum standard en C/C++, et l'absence d'une fonction aussi fondamentale en dit long sur ces langages.
En dehors de cela, je crois que les deux points de vue majoritaires sur la bonne approche pour définir une telle fonction sont en quelque sorte corrects, et la "controverse" à ce sujet est en réalité un non-argument une fois que vous prenez en compte deux mises en garde importantes:
Une fonction signum doit toujours renvoyer le type de son opérande, de la même manière qu'une fonction abs()
, car signum est généralement utilisé pour la multiplication avec une valeur absolue après celle-ci. a été traité en quelque sorte. Par conséquent, le principal cas d'utilisation de signum n'est pas une comparaison, mais une arithmétique, cette dernière ne devant impliquer aucune conversion coûteuse en nombres entiers/en points flottants.
Les types à virgule flottante ne comportent pas une valeur zéro exacte unique: +0.0 peut être interprété comme "infiniment supérieur à zéro" et -0.0 comme "infiniment inférieur à zéro". C'est la raison pour laquelle les comparaisons impliquant zéro doivent être vérifiées en interne par rapport aux deux valeurs, et une expression telle que x == 0.0
peut être dangereuse.
En ce qui concerne C, je pense que le meilleur moyen d’aller de l’avant pour les types intégraux consiste à utiliser l’expression (x > 0) - (x < 0)
, car elle devrait être traduite sans branches et ne nécessite que trois opérations de base. Définissez au mieux les fonctions en ligne qui appliquent un type de retour correspondant au type d'argument et ajoutez un C11 define _Generic
pour mapper ces fonctions à un nom commun.
Avec les 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'elles sont également très susceptibles de ne pas avoir de branches, De plus, il n'est pas nécessaire de convertir le résultat d'un entier en une valeur à virgule flottante. Vous devriez probablement dire clairement que vos implémentations en virgule flottante de signum ne renverront pas zéro en raison des particularités des valeurs nulles en virgule flottante, des considérations de temps de traitement, mais aussi parce que cela est souvent très utile en arithmétique en virgule flottante. recevoir le bon signe -1/+ 1, même pour des valeurs nulles.
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?
La réponse acceptée avec la surcharge ci-dessous ne déclenche en effet pas - 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>());
}
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, cela ne déclenche aucun avertissement sur GCC 5.3.1.
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 ) )
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 depuis "standard" sgn (), car elle est le plus souvent utilisée dans le code suivant:
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 transtypage pour les types non signés et pas de désavantage 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);
}
Voici une implémentation conviviale pour les branches:
inline int signum(const double x) {
if(x == 0) return 0;
return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
À moins que la moitié des chiffres ne soit égal à zéro dans vos données, le prédicteur de branche choisira l'une des branches comme étant la plus courante. Les deux branches ne comportent que des opérations simples.
Alternativement, sur certains compilateurs et architectures de CPU, une version complètement autonome peut être plus rapide:
inline int signum(const double x) {
return (x != 0) *
(1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
Cela fonctionne pour format à virgule flottante binaire double précision IEEE 754: binary64 .
Bien que la solution entière dans la réponse acceptée soit assez élégante, cela me gênait de ne pas pouvoir renvoyer NAN pour les types doubles, aussi je l'ai légèrement modifiée.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
Notez que le renvoi d'un NAN à virgule flottante, par opposition à un NAN
codé en dur, entraîne la définition du bit de signe dans quelques implémentations , donc la sortie pour val = -NAN
et val = NAN
vont être identiques quoi qu'il en soit (si vous préférez une sortie "nan
" sur un -nan
, vous pouvez mettre un abs(val)
avant le retour ...)
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
Cette fonction suppose:
Vous pouvez utiliser la méthode boost::math::sign()
à partir de boost/math/special_functions/sign.hpp
si le boost est disponible.