Is there a standard sign function (signum, sgn) in C/C++?
我想要一个函数,对于负数返回-1,对于正数返回+1。http://en.wikipedia.org/wiki/sign_函数我自己写这本书很容易,但它似乎应该放在某个标准库的某个地方。
编辑:特别是,我在寻找一个处理浮动的函数。
惊讶的是没有人发布了无分支、类型安全的C++版本:
1 2 3 | template <typename T> int sgn(T val) { return (T(0) < val) - (val < T(0)); } |
效益:
- 实际上实现signum(-1、0或1)。这里使用copysign的实现只返回-1或1,这不是signum。此外,这里的一些实现返回的是浮点(或T),而不是int,这看起来很浪费。
- 适用于int、float、double、unsigned short或任何可从integer 0和orderable构造的自定义类型。
- 快!
copysign 的速度很慢,特别是当你需要提升然后再缩小的时候。这是无分支的,优化非常出色 - 符合标准!bitshift hack很简洁,但只适用于某些位表示,并且在有无符号类型时不起作用。在适当的时候,它可以作为手动专业化提供。
- 准确!与零的简单比较可以保持机器的内部高精度表示(例如x87上的80位),并避免过早的归零。
Caveats:
- 它是一个模板,因此需要永远编译。
- 显然,有些人认为使用一个新的、有点深奥的、非常缓慢的标准库函数,甚至不真正实现signum,这是可以理解的。
当为无符号类型实例化时,检查的
< 0 部分触发GCC的-Wtype-limits 警告。您可以通过使用一些重载来避免这种情况:1
2
3
4
5
6
7
8
9
10
11
12
13
14template <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 | (x > 0) - (x < 0) |
以下是一种更易读的方法:
1 2 3 | if (x > 0) return 1; if (x < 0) return -1; return 0; |
如果您喜欢三元运算符,可以这样做:
1 | (x > 0) ? 1 : ((x < 0) ? -1 : 0) |
有一个名为copysign()的c99数学库函数,它从一个参数中获取符号,从另一个参数中获取绝对值:
1 2 3 | 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++ EDCOX1×3函数。
似乎大多数答案都漏掉了原来的问题。
Is there a standard sign function (signum, sgn) in C/C++?
在标准库中没有,但是有可以通过
1 2 3 4 5 | #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
比上述解决方案更快,包括最高评级的解决方案:
1 | (x < 0) ? -1 : (x > 0) |
Is there a standard sign function (signum, sgn) in C/C++?
是的,取决于定义。
C99及更高版本的
int signbit (real-floatingx );
Thesignbit macro returns a nonzero value if and only if the sign of its argument value is negative. C11 §7.12.3.6
但是OP想要一些不同的东西。
I want a function that returns -1 for negative numbers and +1 for positive numbers. ... a function working on floats.
1 | #define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1) |
更深的:
在以下情况下,该职位并不具体,
经典的
许多答案已经涵盖了这一点,但没有涉及到
典型答案在
1 2 3 4 5 | int signnum_typical(double x) { if (x > 0.0) return 1; if (x < 0.0) return -1; return 0; } |
相反,建议使用此功能:在
1 2 3 4 5 | double signnum_c(double x) { if (x > 0.0) return 1.0; if (x < 0.0) return -1.0; return x; } |
有一种不需要分支就可以完成的方法,但它不是很漂亮。
1 | sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)); |
http://graphics.斯坦福.edu/~seander/bithacks.html
那一页上还有很多其他有趣的,非常聪明的东西…
如果您只想测试符号,请使用signbit(如果其参数有负号,则返回true)。不知道为什么您特别希望返回-1或+1;copysign更方便但在某些平台上,它会返回+1,表示负零。只有部分支持负零,其中signbit可能返回true。
一般来说,C/C++中没有标准的符号函数,而缺少这样一个基本的功能,这说明了很多语言。
除此之外,我相信关于定义这样一个函数的正确方法的大多数观点都是正确的,而且一旦考虑到两个重要的警告,"争论"实际上是没有争议的:
signum函数应始终返回其操作数的类型,类似于
abs() 函数,因为signum通常用于在以某种方式处理后与绝对值相乘。因此,符号的主要用例不是比较而是算术,后者不应该涉及任何昂贵的整数到/从浮点转换。浮点类型没有一个精确的零值:+0.0可以解释为"零上无穷大",-0.0可以解释为"零下无穷大"。这就是为什么涉及零的比较必须在内部检查这两个值的原因,并且像EDOCX1[1]这样的表达式可能很危险。
关于C,我认为使用整型的最好方法确实是使用
对于浮点值,我认为基于c11
简而言之,我复制的C揭示了一个称为copysign的标准函数的存在,这可能是有用的。看起来copysign(1.0,-2.0)将返回-1.0,copysign(1.0,2.0)将返回+1.0。
很接近啊?
下面的重载接受的答案确实不会触发-wtype限制,但是它会在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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,另一种选择可能是。
1 2 3 4 5 6 7 8 9 10 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中的任何警告。
不,它不存在于C++中,就像在Matlab中一样。为此,我在程序中使用了宏。
1 | #define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) ) |
有点偏离主题,但我用这个:
1 2 3 4 5 6 7 8 9 | 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()中更有用,因为它最常用于这样的代码:
1 2 3 | int comp(unsigned a, unsigned b){ return sgn( int(a) - int(b) ); } |
VS
1 2 3 | int comp(unsigned a, unsigned b){ return sgn(a, b); } |
无符号类型没有强制转换,也没有其他减号。
实际上,我有一段代码使用sgn()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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); } |
如果有增强,可以使用
下面是一个支持分支的实现:
1 2 3 4 | inline int signum(const double x) { if(x == 0) return 0; return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1)); } |
除非数据的0是数字的一半,否则分支预测器将选择其中一个分支作为最常见的分支。两个分支只涉及简单的操作。
或者,在某些编译器和CPU体系结构上,完全无分支的版本可能更快:
1 2 3 4 | inline int signum(const double x) { return (x != 0) * (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1)); } |
这适用于IEEE754双精度二进制浮点格式:binary64。
1 2 3 4 5 | int sign(float n) { union { float f; std::uint32_t i; } u { n }; return 1 - ((u.i >> 31) << 1); } |
此函数假定:
- 浮点数的二进制32表示法
- 使用命名联合时,对严格别名规则进行异常处理的编译器。
虽然接受的答案中的整数解决方案相当优雅,但它无法为双类型返回NaN,这让我感到困扰,因此我对它进行了轻微的修改。
1 2 3 | template <typename T> double sgn(T val) { return double((T(0) < val) - (val < T(0)))/(val == val); } |
注意,与硬编码的
为什么要使用三元运算符,如果不是,当您可以简单地执行此操作时
1 | #define sgn(x) x==0 ? 0 : x/abs(x) |
我就在今天碰到这个。好吧,没有标准的方法,但是…
既然OP只需要放大输出范围并将其重新集中在0上,(-1到1而不是0到1),为什么不把它加倍并减去1呢?
我用过这个:
(x<0)*2-1
或者,强制进行位移位:
(x<0)<1-1
但无论如何,编译器可能会对此进行优化。
1 | double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); } |
如何:
1 | int sgn = x/fabs(x); |
结果应该不错。
用途:
1 | `#define sgn(x) (x<0)` |
例如:
1 | `if(sng(n)) { etc ....}` |
或者您可能希望使用一些详细的代码,但首先要强制转换:
{
return ((x<0)? true: false);
}