可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want a function that returns -1 for negative numbers and +1 for positive numbers.
http://en.wikipedia.org/wiki/Sign_function
It\'s easy enough to write my own, but it seems like something that ought to be in a standard library somewhere.
Edit: Specifically, I was looking for a function working on floats.
回答1:
Surprised no one has posted the branchless, type-safe C++ version yet:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
Benefits:
- Actually implements signum (-1, 0, or 1). Implementations here using copysign only return -1 or 1, which is not signum. Also, some implementations here are returning a float (or T) rather than an int, which seems wasteful.
- Works for ints, floats, doubles, unsigned shorts, or any custom types constructible from integer 0 and orderable.
- Fast!
copysign
is slow, especially if you need to promote and then narrow again. This is branchless and optimizes excellently
- Standards-compliant! The bitshift hack is neat, but only works for some bit representations, and doesn\'t work when you have an unsigned type. It could be provided as a manual specialization when appropriate.
- Accurate! Simple comparisons with zero can maintain the machine\'s internal high-precision representation (e.g. 80 bit on x87), and avoid a premature round to zero.
Caveats:
- It\'s a template so it\'ll take forever to compile.
- Apparently some people think use of a new, somewhat esoteric, and very slow standard library function that doesn\'t even really implement signum is more understandable.
The < 0
part of the check triggers GCC\'s -Wtype-limits
warning when instantiated for an unsigned type. You can avoid this by using some overloads:
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>());
}
(Which is a good example of the first caveat.)
回答2:
I don\'t know of a standard function for it. Here\'s an interesting way to write it though:
(x > 0) - (x < 0)
Here\'s a more readable way to do it:
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
If you like the ternary operator you can do this:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
回答3:
There is a C99 math library function called copysign(), which takes the sign from one argument and the absolute value from the other:
result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double
will give you a result of +/- 1.0, depending on the sign of value. Note that floating point zeroes are signed: (+0) will yield +1, and (-0) will yield -1.
回答4:
Apparently, the answer to the original poster\'s question is no. There is no standard C++ sgn
function.
回答5:
It seems that most of the answers missed the original question.
Is there a standard sign function (signum, sgn) in C/C++?
Not in the standard library, but there is in boost
, which might as well be part of the standard.
#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
回答6:
Faster than the above solutions, including the highest rated one:
(x < 0) ? -1 : (x > 0)
回答7:
There\'s a way to do it without branching, but it\'s not very pretty.
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu/~seander/bithacks.html
Lots of other interesting, overly-clever stuff on that page, too...
回答8:
Is there a standard sign function (signum, sgn) in C/C++?
Yes, depending on definition.
C99 and later has the signbit()
macro in <math.h>
int signbit
(real-floating x
);
The signbit
macro returns a nonzero value if and only if the sign of its argument value is negative. C11 §7.12.3.6
Yet OP wants something a little different.
I want a function that returns -1 for negative numbers and +1 for positive numbers. ... a function working on floats.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
Deeper:
The post is not specific in the following cases, x = 0.0, -0.0, +NaN, -NaN
.
A classic signum()
returns +1
on x>0
, -1
on x>0
and 0
on x==0
.
Many answers have already covered that, but do not address x = -0.0, +NaN, -NaN
. Many are geared for an integer point-of-view that usually lacks Not-a-Numbers (NaN) and -0.0.
Typical answers function like signnum_typical()
On -0.0, +NaN, -NaN
, they return 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;
}
Instead, propose this functionality: On -0.0, +NaN, -NaN
, it returns -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;
}
回答9:
If all you want is to test the sign, use signbit (returns true if its argument has a negative sign).
Not sure why you would particularly want -1 or +1 returned; copysign is more convenient
for that, but it sounds like it will return +1 for negative zero on some platforms with
only partial support for negative zero, where signbit presumably would return true.
回答10:
In general, there is no standard signum function in C/C++, and the lack of such a fundamental function tells you a lot about these languages.
Apart from that, I believe both majority viewpoints about the right approach to define such a function are in a way correct, and the \"controversy\" about it is actually a non-argument once you take into account two important caveats:
A signum function should always return the type of its operand, similarly to an abs()
function, because signum is usually used for multiplication with an absolute value after the latter has been processed somehow. Therefore, the major use case of signum is not comparisons but arithmetic, and the latter shouldn\'t involve any expensive integer-to/from-floating-point conversions.
Floating point types do not feature a single exact zero value: +0.0 can be interpreted as \"infinitesimally above zero\", and -0.0 as \"infinitesimally below zero\". That\'s the reason why comparisons involving zero must internally check against both values, and an expression like x == 0.0
can be dangerous.
Regarding C, I think the best way forward with integral types is indeed to use the (x > 0) - (x < 0)
expression, as it should be translated in a branch-free fashion, and requires only three basic operations. Best define inline functions that enforce a return type matching the argument type, and add a C11 define _Generic
to map these functions to a common name.
With floating point values, I think inline functions based on C11 copysignf(1.0f, x)
, copysign(1.0, x)
, and copysignl(1.0l, x)
are the way to go, simply because they\'re also highly likely to be branch-free, and additionally do not require casting the result from integer back into a floating point value. You should probably comment prominently that your floating point implementations of signum will not return zero because of the peculiarities of floating point zero values, processing time considerations, and also because it is often very useful in floating point arithmetic to receive the correct -1/+1 sign, even for zero values.
回答11:
My copy of C in a Nutshell reveals the existence of a standard function called copysign which might be useful. It looks as if copysign(1.0, -2.0) would return -1.0 and copysign(1.0, 2.0) would return +1.0.
Pretty close huh?
回答12:
No, it doesn\'t exist in c++, like in matlab. I use a macro in my programs for this.
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
回答13:
The accepted Answer with the overload below does indeed not trigger -Wtype-limits however it triggers -Wunused-parameter on the is_signed
argument.
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>());
}
For C++11 an alternative could be.
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));
}
For me it does not trigger any warnings on GCC 5.3.1
回答14:
Bit off-topic, but I use this:
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));
}
and I found first function - the one with two arguments, to be much more useful from \"standard\" sgn(), because it is most often used in code like this:
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
vs.
int comp(unsigned a, unsigned b){
return sgn(a, b);
}
there is no cast for unsigned types and no additional minus.
in fact i have this piece of code using 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);
}
回答15:
You can use boost::math::sign()
method from boost/math/special_functions/sign.hpp
if boost is available.
回答16:
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
This function assumes:
- binary32 representation of floating point numbers
- a compiler that make an exception about the strict aliasing rule when using a named union
回答17:
#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;
}
The above code may not serve your purpose (getting 1 or -1), but it certainly makes it way more easier to make out the sign of the data type (int, float, double etc)
回答18:
While the integer solution in the accepted answer is quite elegant it bothered me that it wouldn\'t be able to return NAN for double types, so I modified it slightly.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
Note that returning a floating point NAN as opposed to a hard coded NAN
causes the sign bit to be set in some implementations, so the output for val = -NAN
and val = NAN
are going to be identical no matter what (if you prefer a \"nan
\" output over a -nan
you can put an abs(val)
before the return...)
回答19:
Why use ternary operators and if-else when you can simply do this
#define sgn(x) x==0 ? 0 : x/abs(x)
回答20:
Here\'s a branching-friendly implementation:
inline int signum(const double x) {
if(x == 0) return 0;
return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
Unless your data has zeros as half of the numbers, here the branch predictor will choose one of the branches as the most common. Both branches only involve simple operations.
Alternatively, on some compilers and CPU architectures a completely branchless version may be faster:
inline int signum(const double x) {
return (x != 0) *
(1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
This works for IEEE 754 double-precision binary floating-point format: binary64 .
回答21:
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
回答22:
I ran into this just today. So fine, there\'s no standard way but...
Since the OP just needed to magnify the output range and re-centre it on 0, (-1 to 1 not 0 to 1) why not just double it and subtract 1?
I used this:
(x<0)*2-1
Or, forcing a bit shift:
(x<0)<<1-1
But the compiler will likely optimize that anyway.
回答23:
What about:
int sgn = x/fabs(x);
it should work out pretty good.
回答24:
use:
`#define sgn(x) (x<0)`
for example:
`if(sng(n)) { etc ....}`
Or you may want to use some elaborated code, but casting first:
inline bool sgn_long(long x)
{
return ((x<0)? true: false);
}