C / C ++中是否有标准符号函数(signum,sgn)?

我想要一个返回负数的-1和正数+1的函数。 http://en.wikipedia.org/wiki/Sign_function编写自己的代码很容易,但似乎应该是某个标准库中的东西。

编辑:具体来说,我正在寻找一个浮动function的工作。

惊讶的没有人发布了无分类的,types安全的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,这看起来很浪费。
  • 适用于整数,浮点数,双精度,无符号短裤或可从整数0和可订购构造的任何自定义types。
  • 快速! copysign是缓慢的,尤其是如果你需要推动,然后再缩小。 这是无分的优化
  • 符合标准的! bitshift hack是整洁的,但只适用于一些位表示,并且当你有一个无符号types时不起作用。 它可以在适当的时候作为手动专业化提供。
  • 准确! 与零进行简单的比较可以保持机器内部的高精度表示(如x87上的80位),并避免过早的回零。

注意事项:

  • 这是一个模板,所以它将永远需要编译。
  • 显然有些人认为使用一个新的,有点深奥的,很慢的标准库函数,甚至没有真正实现signum是更容易理解的。
  • 实例化为无符号types时,检查的< 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>()); } 

    (这是第一个警告的好例子。)

我不知道它的标准function。 下面是一个有趣的写法:

 (x > 0) - (x < 0) 

这是一个更可读的方式来做到这一点:

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

如果你喜欢三元运算符,你可以这样做:

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

有一个叫做copysign()的C99math库函数,它从一个参数中取符号,从另一个参数取绝对值:

 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。

显然,对原始海报问题的回答是否定的。 没有标准的 C ++ sgn函数。

看来,大部分的答案都错过了原来的问题。

C / C ++中是否有标准符号函数(signum,sgn)?

不在标准库中,但有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

比上述解决scheme更快,包括最高评分的解决scheme:

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

有一种方法可以不分支,但不是很漂亮。

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

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

在页面上还有很多其他有趣的,过于聪明的东西…

如果你想要testing符号,使用signbit (如果它的参数有一个负号,则返回true)。 不知道为什么你特别想要返回-1或+1; copysign更方便,但是它听起来像在某些平台上会返回+1来表示负的零,只有部分支持零负的情况,在这种情况下,signbit大概会返回true。

C / C ++中是否有标准符号函数(signum,sgn)?

是的,取决于定义。

C99和更高版本在<math.h>signbit()macros

int signbit (real-floating x );
当且仅当其参数值的符号为负时, signbitmacros才返回一个非零值。 C11§7.12.3.6


然而,OP想要一些不同的东西。

我想要一个返回负数的-1和正数+1的函数。 一个在花车上工作的函数

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

更深:

这个post在以下情况下不是特定的, x = 0.0, -0.0, +NaN, -NaN

经典signum()x>0上返回+1x>0上返回-1 ,在x==0

许多答案已经涵盖了,但不要解决x = -0.0, +NaN, -NaN 。 许多是针对一个整数的观点,通常缺less非数字( NaN )和-0.0 。

典型的答案像signnum_typical() On -0.0, +NaN, -NaN ,它们返回-0.0, +NaN, -NaN

 int signnum_typical(double x) { if (x > 0.0) return 1; if (x < 0.0) return -1; return 0; } 

相反,build议这个function:在-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; } 

一般来说,在C / C ++中没有标准的符号函数,缺less这样一个基本的函数会告诉你很多这些语言。

除此之外,我相信大多数关于定义这种function的正确方法的观点是正确的,一旦考虑到两个重要的警告,关于它的“争论”实际上是一个无争论的问题:

  • 一个符号函数应该总是返回它的操作数的types,类似于一个abs()函数,因为signum通常用于绝对值的乘法,后者已经被处理了。 因此, signum的主要用例不是比较而是算术,后者不应该包含任何昂贵的整数到浮点转换。

  • 浮点types不具有单个精确的零值:+0.0可以解释为“无限小于零”,-0.0可解释为“无限小于零”。 这就是为什么涉及零的比较必须内部检查这两个值的原因,像x == 0.0这样的expression式可能是危险的。

关于C,我认为使用整型types的最好方法是使用(x > 0) - (x < 0)expression式,因为它应该以无分支方式转换,并且只需要三个基本操作。 最佳定义内联函数,强制匹配参数types的返回types,并添加一个C11 define _Generic将这些函数映射到一个公共名称。

对于浮点值,我认为基于C11 copysignf(1.0f, x)copysign(1.0, x)copysignl(1.0l, x)内联函数是copysignf(1.0f, x) ,因为它们也很有可能是无分支的,另外也不需要将整数结果转换回浮点值。 你可能应该注意到,由于浮点数零值的特殊性,处理时间的考虑以及浮点algorithm在接收正确的-1 / +时, signum的浮点实现不会返回零。 1符号,即使是零值。

我在一个简单的C的副本揭示了一个称为copysign的标准函数的存在,这可能是有用的。 它看起来好像copysign(1.0,-2.0)会返回-1.0,而copysign(1.0,2.0)会返回+1.0。

相当接近吧?

不,它不存在于C ++中,就像在matlab中一样。 我在我的程序中使用了一个macros。

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

下面的超载接受的答案确实不会触发-Wtype-limits,但是它会触发is_signed参数上的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>()); } 

对于C ++ 11,可以select一个替代scheme。

 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上不会触发任何警告

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

这个函数假定:

  • binary32表示浮点数
  • 一个编译器在使用一个已命名的联合时对严格的别名规则进行例外

有点脱离主题,但我使用这个:

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

没有为无符号types转换,也没有额外的减法。

实际上我有这段代码使用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); } 
 #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),但它确实使得更容易确定数据types的符号(int,float,double等)

虽然在接受的答案整数解决scheme是非常优雅的困扰我,它不能返回双types的NAN,所以我稍微修改它。

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

注意返回一个浮点数NAN而不是一个硬编码的NAN会导致符号位在某些实现中被设置,所以val = -NANval = NAN的输出将会是相同的(如果你更喜欢“ nan ”输出-nan可以在返回之前放一个abs(val) …)

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

我今天遇到了这个。 很好,没有标准的方法,但…

由于OP只需放大输出范围并将其重新居中于0(-1到1而不是0到1),为什么不把它加倍并减去1呢?

我用这个:

(X <0)* 2-1

或者,迫使有点转变:

(X <0)<< 1-1

但编译器可能会优化。

关于什么:

 int sgn = x/fabs(x); 

它应该工作得很好。

使用:

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

例如:

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

或者你可能想要使用一些详细的代码,但首先投射:

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