Est-il un standard de la fonction signe (signum, sgn) en C/C++?
Je veux une fonction qui renvoie la valeur -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, mais il semble comme une chose qui devrait être dans une bibliothèque standard quelque part.
Edit: plus Précisément, j'étais à la recherche d'une fonction de travail sur les flotteurs.
- Que devrait-il retourner pour 0?
- McQueen; cela dépend si c'est positif, nul ou négatif zéro.
- J'ai remarqué que vous avez spécifié la valeur de retour comme un entier. Vous êtes à la recherche d'une solution qui prend des entiers ou des nombres à virgule flottante?
- Vrai pour les chars, mais pas pour les entiers, donc je suppose que la question est: est l'interlocuteur intéressé par des flotteurs ou ints?
- McQueen, false pour les flotteurs de trop, non? sgn(x)'s définition dit de renvoyer 0 si
x==0
. Selon IEEE 754, zéro négatif et positif zéro devrait considérées comme égales. - Je ne comprends pas votre commentaire. ce que vous dites est faux?
- dépend positif, nul ou négatif zéro". En fait, il ne le fait pas.
- Le
sgn()
de votre comment est un point de vue raisonnable de la façon de traiter les zéros. OP seulement indirectement spécifié signe de zéro avec un lien. Un autre point de vue raisonnable que le "signe" est de retour 1,-1 à +zéro, zéro. Parfois le signe d'un zéro fait une différence. Il dépend des besoins de l'utilisateur. Aussi NaN est une compte que l'OP spécifiéfloat
. - Commentaires en retard, mais en ce qui concerne signé zéros, une autre option raisonnable est que sgn(x) retourne x, lorsque x est égal à zéro. En d'autres termes, vous obtenez 0, mais c'est un signé zéro avec le même signe que l'entrée. @RJFalconer Dans les cas relativement rares qui a signé les zéros de la matière, vous obtenez une réponse sensée, et dans l'autre cas, il ne fait aucune différence.
- Ce résultat doit être retourné pour une valeur NaN?
- C ou C++ - choisir un, pas deux. Ils sont des langues différentes.
- C99, C++ : cplusplus.com/reference/cmath/signbit
Vous devez vous connecter pour publier un commentaire.
Surpris personne n'a posté le type-safe C++ version:
Avantages:
copysign
est lent, surtout si vous avez besoin de promouvoir et puis affinez à nouveau. C'est sans branches et optimise parfaitementMises en garde:
La
< 0
partie des déclencheurs de vérification du CCG-Wtype-limits
d'avertissement lorsqu'il est instancié pour un type non signé. Vous pouvez éviter cela en utilisant certaines surcharges:(Qui est un bon exemple de la première mise en garde.)
T
.std::copysign
semble résulter dans excellent code pour moi: 4 instructions (inline), pas de ramification, entièrement à l'aide de la FPU. La recette donnée dans cette réponse, en revanche, génère bien pire code (beaucoup plus d'instructions, y compris une multiplication, l'aller-retour entre entier de l'unité et de la FPU)...copysign
sur un int, il favorise float/double, et doivent réduire de nouveau sur le retour. Votre compilateur peut optimiser la promotion, mais je ne trouve rien qui suggère que garanti par la norme. Aussi pour mettre en œuvre signum via copysign vous devez manuellement la poignée de l'0 cas - s'il vous plaît assurez-vous d'inclure cela dans toute comparaison des performances.return (T(0) < val) - (val < T(0));
il ne nécessiteoperator<
défini. Vous pouvez également éviter d'avoir deux temporaires (et constructeurs) en attribuantT(0)
temporaire explicitement. Le compilateur ne peut pas faire cette optimisation pour les types plus complexesT
.operator<
. Maisconstexpr
fonctions organismes sont tenus d'avoir seulement une instruction de retour (+using
/typedef
), de sorte que vous avez à choisir entre constexpr et l'optimisation cher constructeurs; dans ce cas, je pense constexpr est plus utile.bool
s conforme à la norme?int
.false
convertit à zéro ettrue
convertit à un.true
convertit à1
, et pas seulement pour une mise en œuvre définies par une valeur non nulle?cmov
ou de prédication pour éviter une branche. Sur x86 cela peut trivialement être dépourvu de branches avecsetg
, comme illustré ici: godbolt.org/g/XNst9Z je ne sais pas trop sur PowerPC ou de BRAS, mais de jouer sur godbolt.org il semble être dépourvu de branches là aussi.Je ne sais pas d'une fonction de base pour cela. Voici une façon intéressante de l'écrire si:
Voici un moyen plus lisible pour le faire:
Si vous aimez l'opérateur ternaire vous pouvez faire ceci:
x==0
.?:
expressions non-ramification du code machine.x ? (x < 0) ? -1 : 1 : 0
1
en C?<
,>
... ne donne 1 si la relation est vraie et 0 si elle est fausse"0
est "false"; toute autre valeur est "true"; cependant, le relationnel et l'égalité des opérateurs de toujours revenir0
ou1
(voir la Norme 6.5.8 et 6.5.9). -- la valeur de l'expressiona * (x == 42)
est soit0
oua
.!
) renvoie 0 ou 1.!!x
est 0 est la valeur dex
est 0, 1 sinon 🙂is*
fonctions déclarées dans<ctype.h>
. 7.4.1/1 "Les fonctions de ce sous-alinéa retour différente de zéro (vrai) si et seulement si ...", afin de ne pas tous "boolean" C fonctions renvoient exclusivement 0 et 1.copysign
intégrale de lax
même si j'avais à disposition.>
et<
subir une branche! Le PROCESSEUR est de comparer les opérandes, puis il a décider de charger un1
ou un0
dans un registre en fonction de ce résultat. Certaines plates-formes pourrait être en mesure d'éviter que s'ils ont une "copie SREG drapeau dans le registre" la fonction, mais ça ne va pas vrai pour toutes les architectures.Il y a un C99 bibliothèque de mathématiques fonction appelée copysign(), qui prend le signe d'un argument et la valeur absolue de l'autre:
vous donnera un résultat de +/- 1.0, en fonction du signe de valeur. Notez que virgule flottante zéros sont signés: (+0) donnera +1, et (-0) donnera -1.
copysign
semble beaucoup mieux le code que toutes les solutions de rechange proposées...copysign()
sur un microcontrôleur AVR, il ajoute une incroyable 334 octets pour la taille du programme par rapport à la "hacks" (si pas déjà en utilisant rien d'autre demath.h
).Il semble que la plupart des réponses manqué à la question d'origine.
Pas dans la bibliothèque standard, mais il est
copysign
qui peut être utilisé presque de la même manière viacopysign(1.0, arg)
et il est un vrai signe de la fonction dansboost
, qui peut aussi bien faire partie de la norme.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
Apparemment, la réponse à l'affiche originale de la question est non. Il n'y a pas de standard C++
sgn
fonction.copysign()
ne rendra pas votre premier paramètre 0.0 si la seconde est 0.0. En d'autres termes, John est correct.Plus rapide que les solutions ci-dessus, y compris la plus haute tension nominale:
(x < 0) ? -1 : (x > 0) ? 1 : 0
? Elle pourrait être moins laconique, mais c'est plus lisible.Oui, selon la définition.
C99 et plus tard a la
signbit()
macro dans<math.h>
Encore l'OP veut quelque chose d'un peu différent.
Plus profond:
Le poste n'est pas spécifique dans les cas suivants,
x = 0.0, -0.0, +NaN, -NaN
.Un classique
signum()
retourne+1
surx>0
,-1
surx>0
et0
surx==0
.Beaucoup de réponses ont déjà couvert, mais n'abordent pas
x = -0.0, +NaN, -NaN
. Nombreux sont conçus pour un nombre entier de point-de-vue qui manque habituellement Pas-un-Nombres (NaN) et -0.0.Des réponses types de fonction comme
signnum_typical()
Sur-0.0, +NaN, -NaN
, ils reviennent0.0, 0.0, 0.0
.Au lieu de cela, proposer cette fonctionnalité: Sur
-0.0, +NaN, -NaN
, il retourne-0.0, +NaN, -NaN
.Il y a un moyen de le faire sans ramification, mais ce n'est pas très jolie.
http://graphics.stanford.edu/~seander/bithacks.html
Beaucoup d'autres intéressant, trop intelligent choses sur cette page, trop...
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
ousign = (v > 0) - (v < 0);
.v
est un type entier, pas plus large que intSi tout ce que vous voulez, c'est de tester le signer, utilisez signbit (renvoie vrai si son argument est un signe négatif).
Pas sûr de savoir pourquoi vous souhaitez particulièrement -1 ou +1 retourné; copysign est plus pratique
pour cela, mais on dirait qu'elle sera de retour +1 pour le négatif, zéro, sur certaines plateformes, avec
uniquement prise en charge partielle négative de zéro, où signbit aurait présumément return true.
if (x < 0)
.En général, il n'existe aucune norme signum fonction en C/C++, et l'absence d'une telle fonction fondamentale, vous en dit beaucoup sur ces langues.
En dehors de cela, je crois tant la majorité des points de vue sur la bonne approche pour définir une telle fonction, d'une façon correcte, et la "polémique", c'est en fait un non-argument une fois que vous prenez en compte deux éléments importants:
Un signum la fonction doit toujours retourner le type de son opérande, de manière similaire à un
abs()
fonction, parce que signum est généralement utilisé pour la multiplication avec une valeur absolue après que ce dernier a été traitée d'une certaine manière. Par conséquent, les principaux cas d'utilisation de signum n'est pas les comparaisons, mais l'arithmétique, et ce dernier ne devrait pas impliquer cher entier-à/de-floating-point de conversions.À virgule flottante ne sont pas fonction d'une seule exacte valeur de zéro: +0,0 peut être interprété comme "infiniment au-dessus de zéro", et -0.0 comme "infiniment au-dessous de zéro". C'est la raison pour laquelle les comparaisons impliquant zéro doit vérifier en interne contre les deux valeurs, et une expression comme
x == 0.0
peut être dangereux.Concernant C, je pense que la meilleure façon de procéder avec les types intégraux est en effet d'utiliser la
(x > 0) - (x < 0)
expression, comme il devrait être traduit dans une branche-mode gratuit, et ne nécessite que trois opérations de base. Mieux définir les fonctions inline, qui imposent un type de retour correspondant au type d'argument, et ajouter un C11define _Generic
à la carte de ces fonctions à un nom commun.Avec des valeurs à virgule flottante, je pense que les fonctions inline basé sur C11
copysignf(1.0f, x)
,copysign(1.0, x)
, etcopysignl(1.0l, x)
sont la voie à suivre, tout simplement parce qu'ils sont aussi très susceptibles d'être la branche libre, et de plus, ils ne nécessitent pas de casting le résultat de l'entier de retour dans une valeur à virgule flottante. Vous devriez probablement commentaire en évidence que votre virgule flottante implémentations de signum ne sera pas de retour à zéro en raison des particularités de la virgule flottante zéro des valeurs, de la transformation des critères de temps, et aussi parce qu'il est souvent très utile en arithmétique à virgule flottante pour recevoir le bon de -1/+1 signe, même pour des valeurs égales à zéro.Ma copie de C en un Mot révèle l'existence d'une fonction standard appelé copysign qui pourrait être utile. Il semble que si copysign(1.0, -2.0) serait de retour -1.0 et copysign(1.0, 2.0) serait de retour +1.0.
Assez proche hein?
Non, il n'existe pas en c++, comme dans matlab. J'utilise une macro dans mes programmes pour cela.
#define sign(x) (((x) > 0) - ((x) < 0))
qui est trop bonne.La accepté de Répondre à la surcharge de travail ci-dessous n'est en effet pas déclencher -Wtype-limites.
Pour le C++11 une solution de rechange pourrait être.
Pour moi, il ne déclenche pas de mises en garde sur GCC 5.3.1.
-Wunused-parameter
avertissement suffit d'utiliser les paramètres sans nom.Peu hors-sujet, mais j'utilise ceci:
et j'ai trouvé la première fonction avec deux arguments, d'être beaucoup plus utile de "standard" sgn(), car c'est le plus souvent utilisé dans le code comme ceci:
vs
il n'y a pas de casting pour les types supplémentaires et pas moins.
en fait, j'ai ce morceau de code à l'aide de sgn()
Cette fonction suppose:
copysign
; si vous utilisez desstatic_assert
vous avez C++11, et peut ainsi vraiment utilisercopysign
.Tandis que la partie entière de la solution dans l'acceptation de réponse est très élégant, il me dérange qu'il ne soit pas en mesure de retourner NAN pour des types double, alors je l'ai modifié légèrement.
Noter que le retour à virgule flottante NAN, par opposition à une codés en dur
NAN
provoque le bit de signe à mettre en certaines implémentations, la sortie pourval = -NAN
etval = NAN
vont être identique n'importe quoi (si vous préférez une "nan
" sortie sur une-nan
vous pouvez mettre unabs(val)
avant le retour...)Vous pouvez utiliser
boost::math::sign()
méthode deboost/math/special_functions/sign.hpp
si le boost est disponible.Voici une ramification d'application facile à utiliser:
À moins que vos données ont de zéros que la moitié des nombres, ici, la direction de la prédicteur choisit l'une des branches les plus courantes. Les deux branches ne concernent que les opérations simples.
Sinon, sur certains compilateurs et des architectures des processeurs totalement dépourvu de branches version peut être plus rapide:
Cela fonctionne pour La norme IEEE 754 double précision binaire à virgule flottante format: binary64 .
Pourquoi utiliser des opérateurs ternaires et si-sinon, quand vous pouvez simplement faire ce