Существует ли стандартная функция знака (signum, sgn) в C/С++?
Мне нужна функция, которая возвращает -1 для отрицательных чисел и +1 для положительных чисел.
http://en.wikipedia.org/wiki/Sign_function
Достаточно легко написать свои собственные, но это похоже на то, что должно быть где-то в стандартной библиотеке.
Изменить: В частности, я искал функцию, работающую с поплавками.
Ответы
Ответ 1
Удивленный никто еще не опубликовал версию С++ без вежливости:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
Преимущества:
- На самом деле реализует signum (-1, 0 или 1). Реализации здесь, используя copysign, возвращают только -1 или 1, что не является signum. Кроме того, некоторые реализации здесь возвращают float (или T), а не int, что кажется расточительным.
- Работает для ints, floats, double, unsigned shorts или любых настраиваемых типов, которые можно построить из целого числа 0 и упорядочиваемого.
- Быстро!
copysign
медленный, особенно если вам нужно продвигать, а затем снова сузить. Это бесветровое и прекрасно оптимизируется
- соответствующий стандартам! Хит взлома является аккуратным, но работает только для некоторых представлений бит и не работает, когда у вас есть неподписанный тип. В случае необходимости он может быть предоставлен в качестве ручной специализации.
- Точная! Простые сравнения с нулем могут поддерживать внутреннее высокоточное представление машины (например, 80 бит на x87) и избегать преждевременного раунда до нуля.
Предостережения:
- Это шаблон, поэтому он будет навсегда компилироваться.
- По-видимому, некоторые люди думают, что использование новой, несколько эзотерической и очень медленной стандартной библиотечной функции, которая даже не реализует signum, более понятно.
-
Часть тега < 0
содержит триггеры GCC -Wtype-limits
при создании экземпляра для неподписанного типа. Вы можете избежать этого, используя некоторые перегрузки:
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>());
}
(Это хороший пример первой оговорки.)
Ответ 2
Я не знаю стандартной функции для него. Здесь интересный способ написать это:
(x > 0) - (x < 0)
Здесь более читаемый способ сделать это:
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
Если вам нравится тернарный оператор, вы можете сделать это:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
Ответ 3
Существует математическая библиотека C99, называемая copysign(), которая принимает знак от одного аргумента и абсолютное значение от другого:
result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double
даст вам результат +/- 1.0, в зависимости от знака значения. Заметим, что нули с плавающей запятой подписаны: (+0) будет давать +1, а (-0) будет равно -1.
Ответ 4
По-видимому, ответ на исходный вопрос с плакатом - нет. Нет стандартной функции С++ sgn
.
Ответ 5
Кажется, что большинство ответов пропустили оригинальный вопрос.
Есть ли стандартная функция знака (signum, sgn) в C/C++?
Не в стандартной библиотеке, однако есть copysign
который можно использовать почти таким же образом через copysign(1.0, arg)
и есть функция true sign в boost
, которая также может быть частью стандарта.
#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
Ответ 6
Быстрее, чем вышеупомянутые решения, включая наивысший рейтинг:
(x < 0) ? -1 : (x > 0)
Ответ 7
Есть ли стандартная функция знака (signum, sgn) в C/С++?
Да, в зависимости от определения.
C99 и позже имеет макрос signbit()
в <math.h>
int signbit
(real-floating x
);
Макрос signbit
возвращает ненулевое значение тогда и только тогда, когда знак его аргумента отрицателен. C11 §7.12.3.6
Тем не менее, OP хочет что-то немного другое.
Мне нужна функция, которая возвращает -1 для отрицательных чисел и +1 для положительных чисел.... функция, работающая над поплавками.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
Deeper:
Сообщение не является конкретным в следующих случаях: x = 0.0, -0.0, +NaN, -NaN
.
Классический signum()
возвращает +1
на x>0
, -1
на x>0
и 0
на x==0
.
Многие ответы уже рассмотрели это, но не адресуйте x = -0.0, +NaN, -NaN
. Многие из них ориентированы на целую точку зрения, которой обычно не хватает Not-a-Numbers (NaN) и - 0.0.
Типичные ответы такие функции, как signnum_typical()
Вкл -0.0, +NaN, -NaN
, они возвращают 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;
}
Вместо этого предложите эту функцию: В -0.0, +NaN, -NaN
он возвращает -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;
}
Ответ 8
Есть способ сделать это без ветвления, но это не очень красиво.
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu/~seander/bithacks.html
Много других интересных, слишком умных вещей на этой странице тоже...
Ответ 9
Если вы хотите протестировать знак, используйте signbit (возвращает true, если его аргумент имеет отрицательный знак).
Не знаете, почему вы особенно хотели бы вернуть -1 или +1; copysign более удобен
для этого, но похоже, что он вернет +1 для отрицательного нуля на некоторых платформах с
только частичная поддержка отрицательного нуля, где signbit предположительно вернет true.
Ответ 10
В общем случае в C/С++ нет стандартной функции signum, и отсутствие такой фундаментальной функции много говорит об этих языках.
Кроме того, я считаю, что взгляды большинства большинства относительно правильного подхода к определению такой функции в некотором роде правильны, и "противоречие" в этом отношении является фактически не аргументом, как только вы принимаете во внимание два важных оговорки:
-
Функция signum всегда должна возвращать тип своего операнда, аналогично функции abs()
, потому что signum обычно используется для умножения с абсолютным значением после того, как последний был обработан каким-то образом. Таким образом, основной вариант использования signum - это не сравнение, а арифметика, и последнее не должно включать каких-либо дорогостоящих преобразований целых чисел в/из плавающей запятой.
-
Типы с плавающей точкой не имеют единственного точного значения нуля: +0.0 можно интерпретировать как "бесконечно больше нуля" и -0.0 как "бесконечно мало ниже нуля". Причина, по которой сравнения, связанные с нулем, должны внутренне проверять оба значения, а выражение типа x == 0.0
может быть опасным.
Что касается C, я думаю, что лучший способ продвижения с интегральными типами - действительно использовать выражение (x > 0) - (x < 0)
, так как оно должно быть переведено без ветвей и требует только трех основных операций. Лучше определите встроенные функции, которые применяют тип возвращаемого значения, соответствующий типу аргумента, и добавьте C11 define _Generic
, чтобы сопоставить эти функции с общим именем.
С значениями с плавающей запятой я думаю, что встроенные функции, основанные на C11 copysignf(1.0f, x)
, copysign(1.0, x)
и copysignl(1.0l, x)
, - это путь, просто потому, что они также, скорее всего, не имеют ветки, и дополнительно не требует возврата результата от целочисленного обратно в значение с плавающей запятой. Вы должны, вероятно, заметить, что ваши плавающие версии signum не возвратят ноль из-за особенностей значений нулевой точки с плавающей запятой, соображений времени обработки, а также потому, что часто очень полезно в арифметике с плавающей запятой получать правильные -1/+ 1, даже для нулевых значений.
Ответ 11
Моя копия C в двух словах показывает существование стандартной функции, называемой copysign, которая может быть полезна. Похоже, что copysign (1.0, -2.0) вернет -1.0, а copysign (1.0, 2.0) вернет +1.0.
Довольно близко, а?
Ответ 12
Нет, он не существует в С++, как в matlab. Я использую макрос в своих программах для этого.
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
Ответ 13
Принятый ответ с приведенной ниже перегрузкой действительно не вызывает -Wtype-limit.
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>());
}
Для С++ 11 альтернативой может быть.
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));
}
Для меня это не вызывает никаких предупреждений на GCC 5.3.1.
Ответ 14
Бит вне темы, но я использую это:
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));
}
и я нашел первую функцию - с двумя аргументами, более полезную из "стандартного" sgn(), потому что она чаще всего используется в коде следующим образом:
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
против.
int comp(unsigned a, unsigned b){
return sgn(a, b);
}
для неподписанных типов нет никакого заливки и никакого дополнительного минуса.
на самом деле у меня есть эта часть кода, используя 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);
}
Ответ 15
Вы можете использовать метод boost::math::sign()
из boost/math/special_functions/sign.hpp
если boost доступен.
Ответ 16
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
Эта функция предполагает:
- двоичное представление чисел с плавающей запятой
- компилятор, который делает исключение в правиле строжайшего aliasing при использовании именованного объединения
Ответ 17
#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;
}
Вышеприведенный код может не служить вашей цели (получение 1 или -1), но это, конечно, упрощает оформление знака типа данных (int, float, double и т.д.)
Ответ 18
Несмотря на то, что целочисленное решение в принятом ответе довольно элегантно, это беспокоило меня, что он не сможет вернуть NAN для двойных типов, поэтому я немного изменил его.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
Обратите внимание, что возврат NAN с плавающей запятой в отличие от жестко закодированного NAN
приводит к тому, что бит знака устанавливается в некоторые реализации, поэтому вывод для val = -NAN
и val = NAN
будет идентичным независимо от того, что (если вы предпочитаете вывод "NAN
" над -nan
, вы можете поставить abs(val)
перед возвратом...)
Ответ 19
Вот дружественная ветвлению реализация:
inline int signum(const double x) {
if(x == 0) return 0;
return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
Если ваши данные не имеют нулей в качестве половины чисел, здесь предиктор ветвлений выберет одну из ветвей в качестве наиболее распространенной. Обе ветки включают только простые операции.
В качестве альтернативы, на некоторых компиляторах и архитектурах ЦП версия без ответвлений может быть быстрее:
inline int signum(const double x) {
return (x != 0) *
(1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
Это работает для двоичного формата с плавающей запятой IEEE 754 с двойной точностью: binary64.
Ответ 20
Зачем использовать троичные операторы и если-иначе, когда вы можете просто сделать это
#define sgn(x) x==0 ? 0 : x/abs(x)
Ответ 21
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
Ответ 22
Я столкнулся с этим только сегодня. Так хорошо, нет стандартного способа, но...
Поскольку OP просто необходим для увеличения диапазона вывода и повторного центрирования его на 0, (от -1 до 1 не от 0 до 1), почему бы просто не удвоить его и вычесть 1?
Я использовал это:
(х < 0) * 2-1
Или, заставляя сдвиг бит:
(х < 0) < < 1-1
Но компилятор, скорее всего, оптимизирует это.
Ответ 23
Как насчет:
int sgn = x/fabs(x);
он должен хорошо работать.
Ответ 24
использование:
`#define sgn(x) (x<0)`
например:
`if(sng(n)) { etc ....}`
Или вы можете использовать какой-то разработанный код, но сначала кастинг:
inline bool sgn_long(long x)
{
return ((x<0)? true: false);
}