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


Potrzebuję funkcji, która zwraca -1 dla liczb ujemnych i +1 dla liczb dodatnich.

http://en.wikipedia.org/wiki/Sign_function
http://en.wikipedia.org/wiki/Sign_function
Łatwo jest napisać własną, ale wygląda na to, że powinna znajdować się gdzieś w standardowej bibliotece.
Edycja: w szczególności szukałem funkcji działającej na pływakach.
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nikogo nie zaskoczyło, że opublikował jeszcze bezgałęziową, bezpieczną dla typów wersję 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 copysign tutaj zwracają tylko wartość -1 lub 1, co nie jest znakiem signum. Ponadto, niektóre implementacje zwracają float (lub T) zamiast int, co wydaje się marnotrawstwem.
  • Działa dla liczb całkowitych, zmiennoprzecinkowych, podwójnych szortów bez znaku lub dowolnych niestandardowych typów analizowanych z liczbą 0 i możliwych do zamówienia.
  • Szybko!
    copysign
    jest powolny, zwłaszcza jeśli chcesz promować, a następnie ponownie zawęzić. Nie ma rozgałęzień i jest doskonale zoptymalizowany
  • Spełnienie! Bitshift jest zgrabny, ale działa tylko dla niektórych reprezentacji bitów i nie działa, gdy masz typ bez znaku. W razie potrzeby można ją przyznać jako specjalizację manualną.
  • Dokładnie! Proste porównanie do zera może zachować wewnętrzną, bardzo precyzyjną reprezentację maszyny (np. 80 bitów na x87) i uniknąć przedwczesnego zaokrąglenia do zera.

Ostrzeżenia:
  • To jest szablon, więc jego kompilacja zajmie wieki.
  • Oczywiście niektórzy myślą, że używając nowej, nieco ezoterycznej i bardzo powolnej funkcji biblioteki standardowej, który nawet nie implementuje signum bardziej jasne.
  • Sprawdzanie części
    & < 0
    podnosi ostrzeżenie GCC
    -Wtype-limits
    podczas tworzenia wystąpienia dla typu bez znaku. Można tego uniknąć w przypadku niektórych przeciążeń:
    template <typename T> inline constexprint signum(T x, std::false_type is_signed) { return T(0) < x;}template <typename T> inline constexprint signum(T x, std::true_type is_signed) { return (T(0) < x) - (x < T(0));}template <typename T> inline constexprint signum(T x) { return signum(x, std::is_signed<T>());}
    (Co jest dobrym przykładem pierwszego zastrzeżenia).
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nie znam standardowej funkcji do tego. Jednak oto interesujący sposób, aby to napisać:
(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ójskładnikowy, możesz to zrobić:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Istnieje funkcja biblioteki matematycznej C99 o nazwie copysign (), która pobiera znak z jednego argumentu i wartość bezwzględną z innego:
result = copysign(1.0, value)// double
result = copysignf(1.0, value)// float
result = copysignl(1.0, value)// long double

da wynik +/- 1,0, w zależności od znaku wartości. Zauważ, że zera zmiennoprzecinkowe są podpisane: (+0) da +1, a (-0) da -1.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Wygląda na to, że większość odpowiedzi pominęła pierwotne pytanie.
Czy istnieje standardowa funkcja znaku (signum, sgn) w C/C ++?
Nie ma go jednak w standardowej bibliotece
copysign
http://en.cppreference.com/w/c ... ysign
którego można używać w bardzo podobny sposób przez
copysign (1.0, arg)
i zawiera funkcję prawdziwego logowania
boost
http://www.boost.org/doc/libs/ ... .html
które również mogą 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/ ... .html
http://www.boost.org/doc/libs/ ... .html
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Najwyraźniej odpowiedź na pytanie pierwotnego autora brzmi: nie. Nie ma

standard

Funkcje C ++
sgn
.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

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

Tak, w zależności od definicji.
C99 i nowsze wersje mają makro
signbit ()
w
& < math.h & >

int signbit
(real-floating
x
);

Makro
signbit
zwraca wartość różną od zera wtedy i tylko wtedy, gdy znak wartości argumentu jest ujemny. C11 §7.12.3.6

A jednak OP chce czegoś innego.

Potrzebuję funkcji, która zwraca -1 dla liczb ujemnych i +1 dla liczb dodatnich. ... funkcja, która działa na pływakach.

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


Głębiej

:
Ta pozycja nie jest specyficzna w następujących przypadkach,
x = 0,0, -0,0, + NaN, -NaN
.
Klasyczny
signum()
https://en.wikipedia.org/wiki/Sign_function
zwraca
+1
do
x & > 0
,
-1
do
x & > 0
i
0
do
x == 0
.
Wiele odpowiedzi już dotyczyło tego pytania, ale nie dotyczyły
x = -0.0, + NaN, -NaN
. Wiele z nich odnosi się do liczby całkowitej punktu widzenia, która zwykle nie zawiera Not-a-Numbers (

NaN
https://en.wikipedia.org/wiki/NaN) i

-0.0
https://en.wikipedia.org/wiki/Signed_zero
.
Typowe odpowiedzi działają jak
signnum_typical ()
do
-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;
}

Zaproponuj zamiast tego tę funkcjonalność: przy
-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;
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Szybciej niż powyższe rozwiązania, w tym te najwyżej oceniane:
(x < 0) ? -1 : (x > 0)
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Można to zrobić bez rozgałęziania, ale nie jest to zbyt ładne.
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));

http://graphics.stanford.edu/~ ... .html
http://graphics.stanford.edu/~ ... rSign
Na tej stronie jest też wiele innych interesujących, zbyt sprytnych rzeczy ...
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jeśli chcesz tylko sprawdzić znak, użyj

signbit
http://www.opengroup.org/onlin ... .html
(zwraca prawdę, jeśli jej argument jest 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 ma zwrócić prawdę.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Moja kopia C pokazuje w skrócie, że istnieje standardowa funkcja 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, prawda?
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Ogólnie rzecz biorąc, w C/C ++ nie ma standardowej funkcji signum, a brak takiej podstawowej funkcji wiele mówi o tych językach.
Ponadto uważam, że oba poglądy większości na prawidłowe podejście do definiowania takiej funkcji są w pewnym sensie poprawne, a „kontrowersje” co do niej w rzeczywistości nie są argumentem, biorąc pod uwagę dwa ważne zastrzeżenia:
  • Funkcjonować signum musi zawsze zwracać typ swojego operandu, podobnie jak
    abs ()
    , ponieważ signum zwykle używany do mnożenia przez wartość bezwzględną po tym, jak ta ostatnia została w jakiś sposób przetworzona. Więc głównym przypadkiem użycia jest signum nie są porównaniami, ale arytmetyką, a ta ostatnia nie powinna obejmować żadnych kosztownych konwersji liczb całkowitych na/ze zmiennoprzecinkowych.
  • Typy zmiennoprzecinkowe nie mają żadnych dokładnych wartości zerowych: +0,0 można interpretować jako „nieskończenie powyżej zera”, a -0,0 jako „nieskończenie poniżej zera”. Dlatego 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 sposobem na wyprzedzenie typów całkowitych jest faktyczne użycie wyrażenia
(x & > 0) - (x & < 0)
, ponieważ musi być tłumaczone bez rozgałęzień i wymaga tylko trzech podstawowych operacji. Najlepszym rozwiązaniem jest zdefiniowanie funkcji wbudowanych, które używają zwracanego typu, który odpowiada typowi argumentu, i dodanie C11
define _Generic
, aby odwzorować te funkcje na wspólną nazwę.
Jeśli chodzi o wartości zmiennoprzecinkowe, myślę, że wbudowane funkcje oparte na C11
copysignf (1.0f, x)
,
copysign (1.0, x)
i
copysignl (1.0l, x)
to właściwa droga, po prostu dlatego, że istnieje duże prawdopodobieństwo, że nie zostaną rozgałęzione, a także nie wymagają rzutowania wyniku w postaci liczby całkowitej z powrotem na zmiennoprzecinkowy. Prawdopodobnie powinieneś zauważyć, że twoje implementacje

signum

zmiennoprzecinkowe nie zwróci zera ze względu na naturę wartości zerowych zmiennoprzecinkowych, względy czasu przetwarzania, a także dlatego, że często jest to bardzo przydatne w arytmetyce zmiennoprzecinkowej, aby uzyskać poprawny znak -1/+ 1, nawet dla wartości zerowych.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nie, nie istnieje w C ++ tak jak w Matlabie. W tym celu używam makra w moich programach.
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Zaakceptowana odpowiedź z poniższym przeciążeniem tak naprawdę nie dzwoni

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

W przypadku C ++ 11 może istnieć alternatywa.
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 budzi to żadnych ostrzeżeń w GCC 5.3.1.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Trochę poza tematem, 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, znacznie bardziej użyteczną ze „standardowej” sgn (), ponieważ najczęściej jest używana w kodzie takim jak ten:
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 rzutowania dla typów bez znaku ani dodatkowego minus.
w rzeczywistości mam ten fragment kodu za pomocą 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);
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Pytanie jest stare, ale teraz jest taka pożądana funkcja. Dodałem otokę z not, lewą zmianą i dec.
Możesz użyć funkcji opakowującej opartej na

szyld z C99
http://www.cplusplus.com/reference/cmath/signbit/
aby uzyskać dokładnie takie zachowanie, jakie chcesz (zobacz kod poniżej).

Zwraca czy znak x jest ujemny.

Można to również zastosować do nieskończonych liczb, NaN i zer (jeśli zero jest bez znaku, jest uważane za dodatnie

<pre class="lang-cpp prettyprint-override">
#include <math.h>int signValue(float a) {
return ((!signbit(a)) << 1) - 1;
}

Uwaga: używam operandu nie („!”), Ponieważ wartość zwracana przez znak bitu nie jest określona jako 1 (chociaż przykłady pozwalają sądzić, że tak będzie zawsze), ale jest to prawda dla liczby ujemnej:

Wartość

Różne od zera (prawda), jeśli znak x jest ujemny; i zero (fałsz) w przeciwnym razie.

Następnie mnożę przez dwa z przesunięciem w lewo („& < <1”), co da nam 2 dla liczby dodatniej i 0 dla liczby ujemnej, a na koniec zmniejszam przez 1, aby otrzymać 1 i -1 dla dodatniej i liczby ujemne, zgodnie z wymaganiami PO.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Chociaż rozwiązanie oparte na liczbach całkowitych w zaakceptowanej odpowiedzi jest całkiem sprytne, martwiłem się, że nie będzie w stanie zwrócić NAN dla typów podwójnych, więc trochę go zmodyfikowałem.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}

Zwróć uwagę, że zwracanie zmiennoprzecinkowego NAN w przeciwieństwie do zakodowanego na stałe
NAN
powoduje ustawienie bitu znaku na

niektóre implementacje
http://ideone.com/VulkXo, więc dane wyjściowe dla
val = -NAN
i
val = NAN
będą identyczne bez względu na wszystko (jeśli wolisz wyjście „
nan
” zamiast
-nan
możesz wstawić
abs (val)
przed powrotem ...)
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

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

Anonimowy użytkownik

Potwierdzenie od:

Oto implementacja przyjazna dla gałęzi:
inline int signum(const double x) {
if(x == 0) return 0;
return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

Jeśli twoje dane nie zawierają zer jako połowy liczb, wówczas predyktor gałęzi wybierze jedną z gałęzi jako najbardziej powszechną tutaj. Obie gałęzie są związane tylko z prostymi operacjami.
Ponadto w przypadku niektórych kompilatorów i architektur procesora w pełni rozwidlona wersja może być szybsza:
inline int signum(const double x) {
return (x != 0) *
(1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

To działa dla

format binarny zmiennoprzecinkowa podwójnej precyzji IEEE 754: binary64
https://en.wikipedia.org/wiki/ ... ary64
.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}

Ta funkcja zakłada, że:
  • binary32 reprezentacja zmiennoprzecinkowa
  • kompilator, który robi wyjątek od reguły ścisła pseudonimizacja za pomocą o imieniu wspomnienia
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

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

Anonimowy użytkownik

Potwierdzenie od:

Po co używać operatorów trójskładnikowych i if-else, skoro można to po prostu zrobić
#define sgn(x) x==0 ? 0 : x/abs(x)

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się