Esiste una funzione di segno standard (signum, sgn) in C / C ++?

Voglio una funzione che restituisce -1 per i numeri negativi e +1 per i numeri positivi. http://en.wikipedia.org/wiki/Sign_function È abbastanza facile scrivere il mio, ma sembra qualcosa che dovrebbe essere in una libreria standard da qualche parte.

Modifica: in particolare, stavo cercando una funzione che funzioni sui float.

Sorpreso, nessuno ha ancora pubblicato la versione C ++ senza tipo di branchless e sicura:

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

Benefici:

  • Attua effettivamente signum (-1, 0 o 1). Le implementazioni qui usando copysign restituiscono solo -1 o 1, che non è il signum. Inoltre, alcune implementazioni qui stanno restituendo un float (o T) piuttosto che un int, che sembra uno spreco.
  • Funziona per interi, float, doppi, cortometraggi senza segno o qualsiasi tipo personalizzato costruibile da intero 0 e ordinabile.
  • Veloce! copysign è lento, soprattutto se è necessario promuovere e quindi restringere nuovamente. Questo è senza fili e ottimizza in modo eccellente
  • Conforms agli standard! L'incisione bitshift è ordinata, ma funziona solo per alcune rappresentazioni di bit e non funziona quando si ha un tipo senza segno. Potrebbe essere fornito come specializzazione manuale quando appropriato.
  • Accurate! Semplici confronti con zero possono mantenere la rappresentazione interna ad alta precisione della macchina (ad es. 80 bit su x87) ed evitare un prematuro round a zero.

Avvertenze:

  • È un modello quindi ci vorrà un'eternità per la compilazione.
  • Apparentemente alcune persone pensano che l'uso di una nuova, piuttosto esoterica e molto lenta funzione di libreria standard che non implementa realmente il signum sia più comprensibile.
  • La parte < 0 del controllo triggers l' -Wtype-limits di GCC -Wtype-limits quando viene istanziato per un tipo senza segno. Puoi evitarlo usando alcuni sovraccarichi:

     template  inline constexpr int signum(T x, std::false_type is_signed) { return T(0) < x; } template  inline constexpr int signum(T x, std::true_type is_signed) { return (T(0) < x) - (x < T(0)); } template  inline constexpr int signum(T x) { return signum(x, std::is_signed()); } 

    (Che è un buon esempio del primo avvertimento.)

Non conosco una funzione standard per questo. Ecco un modo interessante per scriverlo però:

 (x > 0) - (x < 0) 

Ecco un modo più leggibile per farlo:

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

Se ti piace l'operatore ternario puoi farlo:

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

Esiste una funzione di libreria matematica C99 chiamata copysign (), che prende il segno da un argomento e il valore assoluto dall’altro:

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

ti darà un risultato di +/- 1.0, a seconda del segno di valore. Si noti che gli zeri in virgola mobile sono firmati: (+0) produrrà +1 e (-0) produrrà -1.

Apparentemente, la risposta alla domanda del poster originale è no. Non esiste una funzione sgn standard per C ++.

Sembra che la maggior parte delle risposte abbia perso la domanda originale.

Esiste una funzione di segno standard (signum, sgn) in C / C ++?

Non nella libreria standard, ma c’è in boost , che potrebbe anche essere parte dello standard.

  #include  //Returns 1 if x > 0, -1 if x < 0, and 0 if x is zero. template  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

Più veloce delle soluzioni di cui sopra, compresa la più votata:

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

C’è un modo per farlo senza diramazioni, ma non è molto carino.

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

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

Un sacco di altre cose interessanti e troppo intelligenti in quella pagina, anche …

Esiste una funzione di segno standard (signum, sgn) in C / C ++?

Sì, a seconda della definizione.

C99 e signbit() successive ha la macro signbit() in

int signbit ( x reale);
La macro signbit restituisce un valore signbit da zero se e solo se il segno del suo valore argomento è negativo. C11 §7.12.3.6


Tuttavia OP vuole qualcosa di diverso.

Voglio una funzione che restituisce -1 per i numeri negativi e +1 per i numeri positivi. … una funzione che funziona sui float.

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

Più profondo:

Il post non è specifico nei seguenti casi, x = 0.0, -0.0, +NaN, -NaN .

Un classico signum() restituisce +1 su x>0 , -1 su x>0 e 0 su x==0 .

Molte risposte lo hanno già trattato, ma non indirizzano x = -0.0, +NaN, -NaN . Molti sono orientati per un punto di vista intero che di solito manca di Not-a-Numbers ( NaN ) e -0.0 .

Le risposte tipiche funzionano come signnum_typical() Su -0.0, +NaN, -NaN , restituiscono 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; } 

Invece, proponi questa funzionalità: su -0.0, +NaN, -NaN , restituisce -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; } 

Se tutto quello che vuoi è testare il segno, usa signbit (restituisce true se il suo argomento ha un segno negativo). Non sono sicuro del motivo per cui vorresti in particolare -1 o +1 restituito; copysign è più conveniente per questo, ma sembra che restituirà +1 per lo zero negativo su alcune piattaforms con supporto solo parziale per lo zero negativo, dove presumibilmente il segnale restituirebbe true.

In generale, non esiste una funzione di signum standard in C / C ++ e la mancanza di una funzione così fondamentale ti dice molto su questi linguaggi.

A parte questo, credo che entrambi i punti di vista della maggioranza sull’approccio giusto per definire una tale funzione siano in un certo senso corretti, e la “controversia” su di essa è in realtà una non-discussione una volta che si prendono in considerazione due importanti avvertenze:

  • Una funzione signum dovrebbe sempre restituire il tipo del suo operando, analogamente a una funzione abs() , poiché il signum viene solitamente utilizzato per la moltiplicazione con un valore assoluto dopo che quest’ultimo è stato elaborato in qualche modo. Pertanto, il principale caso d’uso del signum non è il confronto ma l’aritmetica, e quest’ultimo non dovrebbe comportare conversioni costose da intero a / da-virgola mobile.

  • I tipi a virgola mobile non presentano un singolo valore zero esatto: +0.0 può essere interpretato come “infinitesimalmente sopra lo zero” e -0.0 come “infinitesimamente sotto lo zero”. Questo è il motivo per cui i confronti che coinvolgono zero devono controllare internamente contro entrambi i valori e un’espressione come x == 0.0 può essere pericolosa.

Per quanto riguarda C, penso che il modo migliore per avanzare con i tipi interi sia in effetti utilizzare l’espressione (x > 0) - (x < 0) , poiché dovrebbe essere tradotta in un modo privo di branch e richiede solo tre operazioni di base. Definire le funzioni inline migliori che impongono un tipo restituito corrispondente al tipo di argomento e aggiungere un C11 define _Generic per mappare queste funzioni su un nome comune.

Con i valori in virgola mobile, penso che le funzioni inline basate su C11 copysignf(1.0f, x) , copysign(1.0, x) e copysignl(1.0l, x) siano la strada da percorrere, semplicemente perché sono anche altamente probabili essere senza branch e inoltre non è necessario eseguire il cast del risultato da intero in un valore in virgola mobile. Probabilmente dovresti commentare in modo prominente che le tue implementazioni in virgola mobile di signum non restituiranno zero a causa delle peculiarità dei valori zero virgola mobile, considerazioni sul tempo di elaborazione e anche perché spesso è molto utile nell'aritmetica in virgola mobile per ricevere il -1 / + corretto 1 segno, anche per valori zero.

La mia copia di C in a Nutshell rivela l’esistenza di una funzione standard chiamata copysign che potrebbe essere utile. Sembra che copysign (1.0, -2.0) restituisca -1.0 e copysign (1.0, 2.0) restituisca +1.0.

Abbastanza vicino eh?

No, non esiste in c ++, come in MATLAB. Io uso una macro nei miei programmi per questo.

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

La risposta accettata con il sovraccarico qui sotto non innesca , tuttavia, i limiti di stile, ma innesca il parametro is_signed sull’argomento is_signed .

 template  inline constexpr int signum(T x, std::false_type is_signed) { return T(0) < x; } template  inline constexpr int signum(T x, std::true_type is_signed) { return (T(0) < x) - (x < T(0)); } template  inline constexpr int signum(T x) { return signum(x, std::is_signed()); } 

Per C ++ 11 un’alternativa potrebbe essere.

 template  typename std::enable_if::value, int>::type inline constexpr signum(T x) { return T(0) < x; } template  typename std::enable_if::value, int>::type inline constexpr signum(T x) { return (T(0) < x) - (x < T(0)); } 

Per me non triggers alcun avviso su GCC 5.3.1

Un po ‘fuori tema, ma io uso questo:

 template constexpr int sgn(const T &a, const T &b) noexcept{ return (a > b) - (a < b); } template constexpr int sgn(const T &a) noexcept{ return sgn(a, T(0)); } 

e ho trovato la prima funzione – quella con due argomenti, per essere molto più utile da “standard” sgn (), perché è usata più spesso in codice come questo:

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

vs.

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

non c’è cast per i tipi non firmati e nessun meno negativo.

in effetti ho questo pezzo di codice usando sgn ()

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

Perché usare gli operatori ternari e if-else quando si può semplicemente fare questo

 #define sgn(x) x==0 ? 0 : x/abs(x) 
 int sign(float n) { union { float f; std::uint32_t i; } u { n }; return 1 - ((ui >> 31) << 1); } 

Questa funzione presuppone:

  • rappresentazione binary32 di numeri in virgola mobile
  • un compilatore che fa un'eccezione alla rigida regola di aliasing quando si usa una unione con nome
 #include #include using namespace std; int main() { float k=10; cout< 

Il codice sopra potrebbe non servire al tuo scopo (ottenere 1 o -1), ma certamente rende molto più facile distinguere il segno del tipo di dati (int, float, double etc)

Mentre la soluzione intera nella risposta accettata è abbastanza elegante, mi ha infastidito il fatto che non sarebbe stato in grado di restituire NAN per i doppi tipi, quindi l’ho leggermente modificato.

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

Si noti che la restituzione di un NAN a virgola mobile rispetto a un NAN codificato rigido determina l'impostazione del bit di segno in alcune implementazioni , pertanto l'output di val = -NAN e val = NAN sarà identico indipendentemente da cosa (se si preferisce un " nan " in uscita su un -nan puoi mettere un abs(val) prima del ritorno ...)

È ansible utilizzare il metodo boost::math::sign() da boost/math/special_functions/sign.hpp se boost è disponibile.

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

Mi sono imbattuto in questo solo oggi. Così bene, non c’è un modo standard ma …

Visto che l’OP aveva solo bisogno di ingrandire l’intervallo di output e ricentrarlo su 0, (da -1 a 1 non da 0 a 1) perché non solo raddoppiarlo e sottrarre 1?

Ho usato questo:

(x <0) * 2-1

Oppure, forzando un po ‘di cambiamento:

(X <0) << 1-1

Ma il compilatore probabilmente lo ottimizzerà comunque.

Che dire:

 int sgn = x/fabs(x); 

dovrebbe funzionare abbastanza bene.

uso:

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

per esempio:

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

O potresti voler usare un codice elaborato, ma prima di lanciare:

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