Is there a standard sign function (signum, sgn) in

2018-12-31 14:46发布

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.

标签: c++ c math
24条回答
残风、尘缘若梦
2楼-- · 2018-12-31 15:20

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...

查看更多
时光乱了年华
3楼-- · 2018-12-31 15:21

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

查看更多
高级女魔头
4楼-- · 2018-12-31 15:24

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.

查看更多
有味是清欢
5楼-- · 2018-12-31 15:25

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?

查看更多
骚的不知所云
6楼-- · 2018-12-31 15:25

You can use boost::math::sign() method from boost/math/special_functions/sign.hpp if boost is available.

查看更多
萌妹纸的霸气范
7楼-- · 2018-12-31 15:27

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.

查看更多
登录 后发表回答