Is There a Standard Sign Function (Signum, Sgn) in C/C++

Is there a standard sign function (signum, sgn) in C/C++?

The type-safe C++ version:

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 might take longer to compile in some circumstances.

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

C++ Sgn Function

return d<-eps?-1:d>eps;

That means:

  • If d is less than -eps, the result is "negative"
  • If d is more than eps, the result is "positive" (d>eps returns 1)
  • Otherwise we return 0 (meaning the number is "zero")

eps would normally be a small number, so we consider numbers between, say -1e-5 and 1e-5 as "practically zero". This approach is used to water down some deficiencies of the computer's floating-point numbers, like that sin(pi)!=0. However, it comes at the cost of introducing an arbitrary number eps in the calculation, and losing the property that eg. if a and b are positive numbers, a*b is positive provided underflow doesn't occur.

Fast sign of integer in C

First of all, integer comparison is very cheap. It's branching that can be expensive (due to the risk of branch mispredictions).

I have benchmarked your function on a Sandy Bridge box using gcc 4.7.2, and it takes about 1.2ns per call.

The following is about 25% faster, at about 0.9ns per call:

int sign(int x) {
return (x > 0) - (x < 0);
}

The machine code for the above is completely branchless:

_sign:
xorl %eax, %eax
testl %edi, %edi
setg %al
shrl $31, %edi
subl %edi, %eax
ret

Two things are worth pointing out:

  1. The base level of performance is very high.
  2. Eliminating branching does improve performance here, but not dramatically.

is there a function in c/c++ that gets integer sign and removes value? [duplicate]

No. It doesn't exist in the standard lib.

fast c++ sign function

As I worn you your test do not measure anything!

From your quick-bench.com link click godbolt icon and see this disassembly.

Note all of your versions are converted to this assembly code:

        movabs  rax, -4600370724363619533 # compile time evaluated result move outside measurement loop
.LBB0_3: # =>This Inner Loop Header: Depth=1
mov qword ptr [rsp + 8], rax
add rbx, -1 # measurement loop counter
jne .LBB0_3

So basically compiler was able to completely remove test code since it noticed all can be const evaluated at compile time!

So you have to feed to test some value which compiler willnot be able determine at compile time.

Here is my attempt to fix your test and its assembly to see what has been optimized. I do not give warranty this measures the right stuff you have to do it your self. Measuring so small and snappy pice of code is relay hard. In fact anything what is executed in so small number of CPU cycles can't be measured precisely and reliably by software.

Zero-crossing Sign function

I would change return 0; to return n;. If n is NaN, sign should return NaN, not 0.



Related Topics



Leave a reply



Submit