我想要一个函数,对于负数返回-1,对于正数返回+1。 http://en.wikipedia.org/wiki/Sign_function 写我自己很容易,但似乎某些东西应该在某个标准库中。
编辑:具体来说,我正在寻找一个处理花车的功能。
答案 0 :(得分:461)
惊讶没有人发布无分支,类型安全的C ++版本:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
优点:
copysign
很慢,特别是如果你需要提升然后再缩小。这是无分支的,并且极好地优化注意事项:
在为无符号类型实例化时,检查的< 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>());
}
(这是第一个警告的一个很好的例子。)
答案 1 :(得分:258)
我不知道它的标准功能。这是一个有趣的写作方式:
(x > 0) - (x < 0)
这是一种更易读的方法:
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
如果你喜欢三元运算符,你可以这样做:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
答案 2 :(得分:180)
有一个名为copysign()的C99数学库函数,它接受一个参数的符号和另一个参数的绝对值:
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。
答案 3 :(得分:72)
显然,原始海报问题的答案是否定的。没有标准 C ++ sgn
函数。
答案 4 :(得分:71)
似乎大多数答案都错过了原来的问题。
C / C ++中是否有标准符号函数(signum,sgn)?
不在标准库中,但是copysign
可以通过copysign(1.0, arg)
几乎以相同的方式使用boost
,http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html中有一个真正的符号函数,也可能是标准的一部分。
#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);
{{3}}
答案 5 :(得分:28)
比上述解决方案更快,包括评分最高的解决方案:
(x < 0) ? -1 : (x > 0)
答案 6 :(得分:19)
C / C ++中是否有标准符号函数(signum,sgn)?
是的,取决于定义。
C99及更高版本在signbit()
<math.h>
宏
int signbit
(实际浮动x
);
当且仅当其参数值的符号为负时,signbit
宏才返回非零值。 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
上的x>0
,-1
上的x>0
和0
上的x==0
。
许多答案已经涵盖了这一点,但未涉及x = -0.0, +NaN, -NaN
。许多人适合整数观点,通常缺少非数字(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;
}
答案 7 :(得分:16)
有一种方法可以在没有分支的情况下完成它,但它不是很漂亮。
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu/~seander/bithacks.html
该页面上还有很多其他有趣,过于聪明的东西......
答案 8 :(得分:11)
如果您只想测试符号,请使用signbit(如果其参数有负号,则返回true)。 不确定为什么你特别希望返回-1或+1; copysign更方便 为此,但听起来它会在某些平台上为负零返回+1 只有部分支持负零,其中signbit可能会返回true。
答案 9 :(得分:5)
一般来说,C / C ++中没有标准的signum函数,缺乏这样的基本函数会告诉你很多这些语言。
除此之外,我认为关于定义这种功能的正确方法的大多数观点都是正确的,一旦你考虑到两个重要的警告,关于它的“争议”实际上是一个非争论: / p>
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)
的内联函数是可行的方法,因为它们很可能是分支-free,另外不需要将整数的结果转换回浮点值。您可能应该突出地评论 signum 的浮点实现不会返回零,因为浮点零值的特殊性,处理时间考虑因素,以及它在浮点运算中通常非常有用接收正确的-1 / + 1符号,即使是零值。
答案 10 :(得分:4)
我在Nutshell中的C副本揭示了一个名为copysign的标准函数的存在,它可能很有用。看起来copysign(1.0,-2.0)将返回-1.0而copysign(1.0,2.0)将返回+1.0。
非常接近吧?
答案 11 :(得分:3)
不,它在c ++中不存在,就像在matlab中一样。我在我的程序中使用了一个宏。
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
答案 12 :(得分:3)
下面超载的已接受答案确实没有触发 -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>());
}
对于C ++ 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上触发任何警告。
答案 13 :(得分:3)
这个问题很旧,但是现在有了这种所需的功能。我添加了一个带有not,左移和dec的包装器。
您可以使用基于signbit from C99的包装函数,以获取确切的所需行为(请参见下面的代码)。
返回x的符号是否为负。
这也可以应用于无限,NaN和零(如果零是无符号的,则视为正数
#include <math.h>
int signValue(float a) {
return ((!signbit(a)) << 1) - 1;
}
注意:我使用操作数不是(“!”),因为signbit的返回值未指定为1(即使示例让我们认为它总是这样),但对于负数为true:>
返回值
如果x的符号为负,则为非零值(true);否则为false。否则为零(false)。
然后我将左移(“ << 1”)乘以2,这将使我们得到2表示正数,0表示负数,最后减1分别得到1和-1(正数和负数)根据OP的要求。
答案 14 :(得分:1)
偏离主题,但我用这个:
connect
我发现第一个函数 - 带有两个参数的函数,从“标准”sgn()开始更有用,因为它最常用于这样的代码:
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));
}
VS
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
没有无符号类型的强制转换,也没有额外的减号。
实际上我使用sgn()
获得了这段代码int comp(unsigned a, unsigned b){
return sgn(a, b);
}
答案 15 :(得分:1)
如果可以使用增强功能,则可以使用boost::math::sign()
中的boost/math/special_functions/sign.hpp
方法。
答案 16 :(得分:0)
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
此功能假定:
答案 17 :(得分:0)
虽然接受的答案中的整数解决方案非常优雅,但是我不能为双重类型返回NAN,所以我稍微修改了它。
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
请注意,返回浮点NAN而不是硬编码NAN
会导致符号位在some implementations中设置,因此val = -NAN
和val = NAN
的输出无论如何都是相同的(如果您希望在nan
上输出“-nan
”,则可以在返回前放置abs(val)
...)
答案 18 :(得分:0)
这是分支友好的实现:
inline int signum(const double x) {
if(x == 0) return 0;
return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
除非数据中的零为数字的一半,否则分支预测器将选择最常见的分支之一。这两个分支都只涉及简单的操作。
或者,在某些编译器和CPU架构上,完全无分支的版本可能会更快:
inline int signum(const double x) {
return (x != 0) *
(1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
这适用于IEEE 754 double-precision binary floating-point format: binary64。
答案 19 :(得分:-1)
为什么只需使用三元运算符和if-else即可
#define sgn(x) x==0 ? 0 : x/abs(x)
答案 20 :(得分:-2)
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
答案 21 :(得分:-2)
我今天遇到了这个问题。很好,没有标准方式,但......
由于OP只需要放大输出范围并将其重新置于0,( - 1到1而不是0到1),为什么不将它加倍并减去1?
我用过这个:
(X小于0)* 2-1
或者,强制转移:
(X小于0)&LT;&LT; 1-1
但是编译器可能会优化它。
答案 22 :(得分:-3)
怎么样:
int sgn = x/fabs(x);
它应该很好。
答案 23 :(得分:-5)
使用:
`#define sgn(x) (x<0)`
例如:
`if(sng(n)) { etc ....}`
或者您可能想要使用一些精心设计的代码,但先进行投射:
inline bool sgn_long(long x)
{
return ((x<0)? true: false);
}