Czy w C/C++istnieje standardowa funkcja znakowa (signum, sgn)?

Chcę funkcji, która zwraca -1 dla liczb ujemnych i +1 dla liczb dodatnich. http://en.wikipedia.org/wiki/Sign_function Łatwo jest napisać własną, ale wydaje się, że to coś, co powinno być gdzieś w standardowej bibliotece.

Edit: konkretnie Szukałem funkcji działającej na pływakach.

 340
Author: batty, 2009-12-15

23 answers

Dziwi mnie, że nikt jeszcze nie opublikował bezgałęziowej, bezpiecznej dla typu wersji C++:

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

Korzyści:

    W rzeczywistości implementuje signum (-1, 0 LUB 1). Implementacje tutaj używające copysign zwracają tylko -1 lub 1, co nie jest signum. Ponadto, niektóre implementacje zwracają float (lub T) zamiast int, co wydaje się marnotrawstwem.
  • Działa dla Int, floats, doubles, unsigned shorts lub dowolnych niestandardowych typów konstruowanych od liczby całkowitej 0 i zamawianych.
  • szybko! copysign jest powolny, zwłaszcza jeśli trzeba promować, a następnie ponownie wąskie. Jest to bezgałęziowe i doskonale optymalizuje
  • zgodny ze standardami! Bitshift hack jest schludny, ale działa tylko dla niektórych reprezentacji bitów i nie działa, gdy masz niepodpisany Typ. W razie potrzeby może być dostarczona jako specjalizacja Manualna.
  • dokładnie! Proste porównania z zero mogą utrzymać wewnętrzną, precyzyjną reprezentację maszyny (np. 80-bitowy na x87) i uniknąć przedwczesnego zaokrąglenia do zera.

Zastrzeżenia:

  • to szablon, więc kompilacja zajmie wieczność.
  • najwyraźniej niektórzy uważają, że użycie nowej, nieco ezoterycznej i bardzo powolnej standardowej funkcji bibliotecznej , która nawet nie implementuje signum {28]} jest bardziej zrozumiałe.
  • Część < 0 podczas tworzenia instancji dla typu niepodpisanego uruchamia Ostrzeżenie GCC -Wtype-limits. Można tego uniknąć, używając pewnych przeciążeń:

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

    (co jest dobrym przykładem pierwszego zastrzeżenia.)

 435
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-28 13:58:41

Nie znam standardowej funkcji. Oto ciekawy sposób na napisanie tego:

(x > 0) - (x < 0)

Oto bardziej czytelny sposób, aby to zrobić:

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

Jeśli podoba Ci się operator trójkowy, możesz to zrobić:

(x > 0) ? 1 : ((x < 0) ? -1 : 0)
 248
Author: Mark Byers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-15 08:19:52

Istnieje funkcja biblioteki matematycznej C99 o nazwie copysign (), która pobiera znak z jednego argumentu, a wartość bezwzględną z drugiego:

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

Daje wynik +/- 1.0, w zależności od znaku wartości. Zauważ, że zera zmiennoprzecinkowe są podpisane: (+0) daje +1, A (-0) daje -1.

 168
Author: comingstorm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-04 01:38:07

Najwyraźniej odpowiedź na pytanie oryginalnego plakatu brzmi nie. Nie istnieje standardowa C++ sgn funkcja.

 67
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 00:24:37

Wydaje się, że większość odpowiedzi przeoczyła pierwotne pytanie.

czy w C/C++istnieje standardowa funkcja znakowa (signum, sgn)?

Nie w bibliotece standardowej, ale jest w boost, co równie dobrze może być częścią standardu.

    #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
Author: Catskul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-01 04:23:45

Szybsze niż powyższe rozwiązania, w tym Najwyżej oceniane:

(x < 0) ? -1 : (x > 0)
 28
Author: xnx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-11 00:02:10

Jest sposób, aby to zrobić bez rozgałęziania, ale nie jest zbyt ładny.

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

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

Wiele innych ciekawych, zbyt mądrych rzeczy na tej stronie, też...

 16
Author: Tim Sylvester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-14 22:52:30

Czy w C/C++istnieje standardowa funkcja znakowa (signum, sgn)?

Tak, w zależności od definicji.

C99 i Później ma signbit() makro w <math.h>

int signbit(real-floating x);
Makro signbit Zwraca wartość niezerową wtedy i tylko wtedy, gdy znak jego wartości argumentu jest ujemny. C11 §7.12.3.6


Jednak OP chce czegoś innego.

Chcę funkcji, która zwraca -1 dla ujemnego Liczby i +1 dla liczb dodatnich. ... funkcja działająca na pływakach.

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

Głębiej:

Post nie jest specyficzny w następujących przypadkach, x = 0.0, -0.0, +NaN, -NaN.

Klasyka signum() returns +1 on x>0, -1 W dniu x>0 i 0 w dniu x==0.

Wiele odpowiedzi już o tym mówiło, ale nie zwracaj się do x = -0.0, +NaN, -NaN. Wiele z nich jest nastawionych na liczbę całkowitą, której zwykle brakuje liczb nie-a ( NaN) i -0.0.

Typowe odpowiedzi działają jak signnum_typical() na -0.0, +NaN, -NaN, zwracają 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;
}

Zamiast tego zaproponuj tę funkcjonalność: na -0.0, +NaN, -NaN, zwraca -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
Author: chux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 20:24:08

Jeśli chcesz przetestować znak, użyj signbit (zwraca true, jeśli jego argument ma znak ujemny). Nie wiem, dlaczego szczególnie chcesz zwrócić -1 lub +1; copysign jest wygodniejszy za to, ale wygląda na to, że zwróci +1 dla ujemnego zera na niektórych platformach z tylko częściowe wsparcie dla ujemnego zera, gdzie signbit prawdopodobnie zwróci true.

 10
Author: ysth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 03:16:31

Ogólnie rzecz biorąc, w C/C++ nie ma standardowej funkcji signum, a brak tak fundamentalnej funkcji wiele mówi o tych językach.

Poza tym, uważam, że oba punkty widzenia większości na temat właściwego podejścia do zdefiniowania takiej funkcji są w pewien sposób poprawne, a "kontrowersja" na jej temat jest w rzeczywistości nie-argumentem, gdy weźmie się pod uwagę dwa ważne zastrzeżenia:

  • A signum funkcja powinna zawsze zwracać typ swojego operandu, podobnie jak funkcja abs(), ponieważ signum jest zwykle używane do mnożenia z wartością bezwzględną po tym, jak ta ostatnia została jakoś przetworzona. Dlatego głównym przypadkiem użycia signum nie są porównania, ale arytmetyka, a te ostatnie nie powinny wiązać się z żadnymi kosztownymi konwersjami liczb całkowitych do/Z zmiennoprzecinkowych.

  • Typy zmiennoprzecinkowe nie zawierają jednej dokładnej wartości zera: +0.0 można interpretować jako "nieskończenie więcej niż zero", a -0.0 jako "nieskończenie poniżej zera". To jest powód, dla którego porównania z zerem muszą wewnętrznie sprawdzać obie wartości, a wyrażenie takie jak x == 0.0 może być niebezpieczne.

Jeśli chodzi o C, myślę, że najlepszym rozwiązaniem dla typów całkowych jest użycie wyrażenia (x > 0) - (x < 0), ponieważ powinno ono być tłumaczone w sposób wolny od gałęzi i wymaga tylko trzech podstawowych operacji. Najlepiej zdefiniować funkcje wbudowane, które wymuszają zwracanie typu odpowiadającego typowi argumentu i dodać C11 define _Generic do mapowania te funkcje do wspólnej nazwy.

Z wartościami zmiennoprzecinkowymi, myślę, że funkcje inline oparte na C11 copysignf(1.0f, x), copysign(1.0, x), i copysignl(1.0l, x) są do zrobienia, po prostu dlatego, że są one również bardzo prawdopodobne, że będą wolne od gałęzi, a dodatkowo nie wymagają odlewania wyniku z liczby całkowitej z powrotem do wartości zmiennoprzecinkowej. Prawdopodobnie powinieneś zauważyć, że Twoje implementacje zmiennoprzecinkowe signum nie zwrócą zera ze względu na specyfikę wartości zmiennoprzecinkowych zero, względy czasu przetwarzania, a także dlatego, że często bardzo przydatne w arytmetyce zmiennoprzecinkowej jest otrzymywanie prawidłowego znaku -1 / +1, nawet dla wartości zerowych.

 5
Author: Tabernakel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-26 13:07:43

Moja kopia C w skrócie ujawnia istnienie standardowej funkcji o nazwie copysign, która może być przydatna. Wygląda na to, że copysign(1.0, -2.0) zwróci -1.0, a copysign (1.0, 2.0) zwróci +1.0.

Całkiem blisko, co?

 4
Author: High Performance Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-22 18:53:38

Nie, nie istnieje w c++, tak jak w matlab. Używam do tego makra w moich programach.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
 3
Author: chattering,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-10 23:35:41

Zaakceptowana odpowiedź z poniższym przeciążeniem rzeczywiście nie uruchamia -wtype-limits, jednak wyzwala -wunused-parametr na argumencie 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>());
}

Dla C++11 alternatywą może być

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

Dla mnie nie uruchamia żadnych ostrzeżeń na GCC 5.3.1

 3
Author: SamVanDonut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-13 17:14:26

Trochę off-topic, ale używam tego:

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

I znalazłem pierwszą funkcję - tę Z dwoma argumentami, która jest znacznie bardziej użyteczna od "standardowej" SGN (), ponieważ jest najczęściej używana w kodzie takim jak:

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

Vs.

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

Nie ma obsady dla typów niepodpisanych i żadnych dodatkowych minusów.

W rzeczywistości mam ten fragment kodu używając 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
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-15 20:03:53

Po co używać operatorów trójdzielnych i if-else, kiedy można to po prostu zrobić

#define sgn(x) x==0 ? 0 : x/abs(x)
 1
Author: Jagreet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
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);
}

Ta funkcja przyjmuje:

  • binary32 reprezentacja liczb zmiennoprzecinkowych
  • kompilator, który tworzy wyjątek o ścisłej zasadzie aliasingu przy użyciu o nazwie union
 0
Author: Gigi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
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;
}

Powyższy kod może nie służyć Twojemu celowi( uzyskanie 1 lub -1), ale z pewnością ułatwia identyfikację znaku typu danych (int, float, double itp.)

 0
Author: kuj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-22 15:41:16

Chociaż rozwiązanie integer w zaakceptowanej odpowiedzi jest dość eleganckie, przeszkadzało mi to, że nie będzie w stanie zwrócić NAN dla podwójnych typów, więc zmodyfikowałem je nieco.

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

Zauważ, że zwracanie zmiennoprzecinkowego NAN w przeciwieństwie do zakodowanego na twardo NAN powoduje, że bit znaku jest ustawiany w niektórych implementacjach , więc wyjście dla val = -NAN i val = NAN będzie identyczne bez względu na wszystko (jeśli wolisz wyjście "nan" zamiast -nan możesz umieścić abs(val) przed / align = "left" / ..)

 0
Author: mrclng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 08:19:11

Możesz użyć metody boost::math::sign() z boost/math/special_functions/sign.hpp jeśli boost jest dostępny.

 0
Author: khkarens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 07:58:19
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
 -1
Author: cyberion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-04 16:05:06

Wpadłem na to właśnie dzisiaj. Więc dobrze, nie ma standard sposób, ale...

Ponieważ OP po prostu potrzebował powiększyć zakres wyjściowy i ponownie wyśrodkować go na 0, (-1 do 1 Nie 0 do 1) Dlaczego po prostu nie podwoić i odjąć 1?

Użyłem tego:

(x

Lub, wymuszając przesunięcie bitowe:

(x

Ale kompilator prawdopodobnie i tak to zoptymalizuje.

 -1
Author: Alidor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 16:27:45

A co z:

int sgn = x/fabs(x);
Powinno się udać.
 -2
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 13:56:09

Użycie:

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

Na przykład:

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

Albo może chcesz użyć jakiegoś rozbudowanego kodu, ale najpierw Wrzuć:

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

 -4
Author: Roberto TUNINETTI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-15 22:25:28