我想要一个函数为负数返回-1,为正数返回+1。 http://en.wikipedia.org/wiki/Sign_function 我自己编写它很容易,但它似乎应该存在于某个标准库中。

编辑:具体来说,我正在寻找一个函数工作在浮动。


当前回答

在C/ c++中是否存在标准符号函数(signum, sgn) ?

是的,这取决于定义。

C99及以后版本在<math.h>中有signbit()宏

Int符号位(实浮点x); signbit宏当且仅当其参数值的符号为负时返回一个非零值。C11§7.12.3.6


然而OP想要一些不同的东西。

我想要一个函数,返回-1的负数和+1的正数. ...处理浮点数的函数。

#define signbit_p1_or_n1(x)  ((signbit(x) ?  -1 : 1)

更深层次的问题:

OP的问题在以下情况下不是特定的:x = 0.0, -0.0, +NaN, -NaN。

经典的signum()在x>0时返回+1,在x<0时返回-1,在x==0时返回0。

许多答案已经涵盖了这一点,但没有涉及x = -0.0, +NaN, -NaN。许多都是针对通常缺少Not-a-Numbers (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;
}

其他回答

如果boost可用,可以使用boost/math/special_functions/sign.hpp中的boost::math::sign()方法。

有一个C99数学库函数叫copysign(),它从一个参数取符号,从另一个参数取绝对值:

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。

这是一个分支友好的实现:

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架构上,完全无分支的版本可能更快:

inline int signum(const double x) {
    return (x != 0) * 
        (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

这适用于IEEE 754双精度二进制浮点格式:binary64。

有一种不用分支的方法,但不太好。

sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));

http://graphics.stanford.edu/~seander/bithacks.html

那一页上还有很多其他有趣的、过于聪明的东西……

有点跑题了,但我用了这个:

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));
}

我发现第一个函数-有两个参数的函数,比“标准”sgn()更有用,因为它最常在这样的代码中使用:

int comp(unsigned a, unsigned b){
   return sgn( int(a) - int(b) );
}

vs.

int comp(unsigned a, unsigned b){
   return sgn(a, b);
}

无符号类型没有强制转换,也没有额外的减号。

这段代码是用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);
}