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);
比上述解决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-floatingx
);
当且仅当其参数值的符号为负时,signbit
macros才返回一个非零值。 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
上返回+1
在x>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 = -NAN
和val = 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); }