Существует ли стандартная функция знака (signum, sgn) в C/C++?
Мне нужна функция, которая возвращает -1 для отрицательных чисел и +1 для положительных чисел. http://en.wikipedia.org/wiki/Sign_function Написать свою книгу достаточно легко, но, похоже, она должна быть где-то в стандартной библиотеке.
Edit: в частности, я искал функцию, работающую на поплавках.
23 ответов
удивлен никто не оставил дистанционных, типобезопасный язык C++ версия:
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, поплавков, двойников, неподписанных шорт или любых пользовательских типов, создаваемых из целого числа 0 и несортируемые.
- быстро!
copysign
медленно, особенно если вам нужно продвигать, а затем снова узким. Это branchless и оптимизирует превосходно - стандартам! Взлом bitshift является аккуратным, но работает только для некоторых битовых представлений и не работает, когда у вас есть тип без знака. При необходимости его можно было бы предусмотреть в качестве ручной специализации.
- точно! Простые сравнения с нул могут поддерживать машину внутреннюю высокоточную представление (например, 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>()); }
(что является хорошим примером первого предостережения.)
Я не знаю стандартной функции для этого. Вот интересный способ написать это:
(x > 0) - (x < 0)
вот более читаемый способ сделать это:
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
Если вам нравится тернарный оператор, вы можете сделать это:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
существует функция математической библиотеки 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.
похоже, что большинство ответов пропустили исходный вопрос.
существует ли стандартная функция знака (signum, sgn) в C/C++?
не в стандартной библиотеке, но есть в 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);
более быстро чем вышеуказанные решения, включая самое высокое расклассифицированное одно:
(x < 0) ? -1 : (x > 0)
есть способ сделать это без ветвления, но это не очень красиво.
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu / ~seander/bithacks.html
много других интересных, слишком умных вещей на этой странице тоже...
существует ли стандартная функция знака (signum, sgn) в C/C++?
да, в зависимости от определения.
C99 и позже имеет signbit()
макрос <math.h>
int signbit
(real-floatingx
);
Thesignbit
макрос возвращает ненулевое значение тогда и только тогда, когда знак его значения аргумента отрицательный. C11 §7.12.3.6
тем не менее OP хочет что-то немного отличающийся.
мне нужна функция, которая возвращает -1 для отрицательных чисел и +1 для положительных чисел. ... функция, работающая на поплавках.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
глубже:
сообщение не является конкретным в следующих случаях,x = 0.0, -0.0, +NaN, -NaN
.
классический signum()
возвращает +1
on x>0
, -1
on x>0
и 0
on x==0
.
многие ответы уже покрыл, что, но не адрес x = -0.0, +NaN, -NaN
. Многие из них ориентированы на целочисленную точку зрения, в которой обычно не хватает не-чисел (Нэн) и -0.0.
типичная функция ответов, как signnum_typical()
On -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;
}
вместо этого предложите эту функциональность: On -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;
}
Если все, что вы хотите, это проверить знак, используйте signbit (возвращает true, если ее аргумент имеет отрицательный знак). Не уверен, почему вы особенно хотите вернуть -1 или +1; copysign более удобен для этого, но похоже, что он вернет +1 для отрицательного нуля на некоторых платформах с только частичная поддержка отрицательного нуля, где signbit предположительно вернет true.
в общем, в C/C++ нет стандартной функции signum, и отсутствие такой фундаментальной функции говорит вам много об этих языках.
кроме того, я считаю, что обе точки зрения большинства о правильном подходе к определению такой функции в некотором роде правильны, и "спор" об этом на самом деле не является аргументом, как только вы учитываете два важных предостережения:
A signum функция должна всегда возвращать тип его операнда, аналогично , потому что 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)
- это путь, просто потому, что они также, скорее всего, будут без ветвей и, кроме того, не требуют приведения результата от integer обратно в значение с плавающей запятой. Вероятно, вы должны прокомментировать, что ваши реализации с плавающей запятой signum не будет возвращать ноль из-за особенностей нулевых значений с плавающей запятой, соображений времени обработки, а также потому, что часто очень полезно в арифметике с плавающей запятой получить правильный знак -1/+1, даже для нулевых значений.
моя копия C в двух словах показывает существование стандартной функции copysign, которая может быть полезна. Похоже, что copysign(1.0, -2.0) вернет -1.0, а copysign (1.0, 2.0) вернет +1.0.
довольно близко, да?
нет, он не существует в c++, как в matlab. Для этого я использую макрос в своих программах.
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
принятый ответ с перегрузкой ниже действительно не вызывает - Wtype-limits однако он запускает -Wunused-параметр на
немного не по теме, но я использую этот:
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));
}
и я нашел первую функцию - ту, у которой два аргумента, что намного полезнее от "standard" sgn (), потому что она чаще всего используется в коде вроде этого:
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
и
int comp(unsigned a, unsigned b){
return sgn(a, b);
}
нет приведения для неподписанных типов и никакого дополнительного минуса.
на самом деле у меня есть этот кусок кода, используя ГГС()
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);
}
зачем использовать тернарные операторы и if-else, когда вы можете просто сделать это
#define sgn(x) x==0 ? 0 : x/abs(x)
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
эта функция предполагает:
- binary32 представление чисел с плавающей запятой
- компилятор, который делает исключение о строгом сглаживании правило при использовании имени Союз
#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 и т. д.)
в то время как целочисленное решение в принятом ответе довольно элегантно, меня беспокоило, что оно не сможет вернуть 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)
до возвращения...)
можно использовать boost::math::sign()
метод boost/math/special_functions/sign.hpp
если boost доступен.
Я столкнулся с этим только сегодня. Так хорошо, нет стандартный пути, но...
Так как OP просто необходимо увеличить выходной диапазон и повторно центрировать его на 0, (-1 до 1 не 0 до 1) Почему бы просто не удвоить его и вычесть 1?
Я использовал этот:
(x
или, заставляя немного сдвинуть:
(x
но компилятор, скорее всего, оптимизирует это в любом случае.
использование:
`#define sgn(x) (x<0)`
например:
`if(sng(n)) { etc ....}`
или вы можете использовать какой-то разработанный код, но сначала кастинг:
inline bool sgn_long(long x)
{
return ((x<0)? true: false);
}