509

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.

batty
  • 7,528
  • 9
  • 31
  • 30
  • 27
    What should it return for 0? – Craig McQueen Dec 14 '09 at 23:26
  • 86
    @Craig McQueen; that depends on if it is a positive zero or negative zero. – ysth Dec 15 '09 at 05:53
  • 1
    I noticed that you specified the return value as an integer. Are you looking for a solution that takes integers or floating point numbers? – Mark Byers Dec 15 '09 at 09:56
  • @ysth: True for floats but not for ints, so I suppose then the question is: is the questioner interested in floats or ints? – Craig McQueen Dec 21 '09 at 07:14
  • 11
    @ysth @Craig McQueen, false for floats too, no? sgn(x)'s [definition](https://en.wikipedia.org/wiki/Signum_function#Definition) says to return 0 if `x==0`. According to [IEEE 754](https://en.wikipedia.org/wiki/Signed_zero#Properties_and_handling), negative zero and positive zero should compare as equal. – RJFalconer Jun 04 '14 at 11:28
  • @RJFalconer: I don't understand your comment. what are you saying is false? – ysth Jun 06 '14 at 05:30
  • 9
    @ysth "it depends on positive zero or negative zero". In fact, it does not. – RJFalconer Jun 06 '14 at 08:10
  • @RJFalconer The `sgn()` of your [comment](http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c/4609795#comment37130168_1903954) is a reasonable view of how to handle zeros. OP only indirectly specified sign of zero with a link. Another reasonable view of the "sign" is to return 1,-1 on +zero,-zero. Sometimes the sign of zero [makes a difference](http://stackoverflow.com/q/25332133/2410359). It depends on user's need. Also [NaN](https://en.wikipedia.org/wiki/NaN) is a [consideration](http://stackoverflow.com/a/35491710/2410359) as OP specified `float`. – chux - Reinstate Monica Jun 13 '16 at 14:01
  • 6
    Commenting late, but regarding signed zeros, another reasonable option is that sgn(x) returns x, when x is zero. In other words, you get 0 out, but it's a signed zero with the same sign as the input. @RJFalconer In the relatively few cases that signed zeros matter, you get a sensible answer, and in the other cases it makes no difference. – Brick Feb 15 '18 at 19:14
  • 1
    What result should be returned for a NaN value? – Toby Speight Nov 19 '18 at 09:32
  • C or C++ - pick one, not both. They are different languages. – Pharap Aug 10 '19 at 14:50
  • C99, C++ : http://www.cplusplus.com/reference/cmath/signbit/ – wcochran Sep 16 '19 at 17:11

21 Answers21

614

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

Stef
  • 13,242
  • 2
  • 17
  • 28
  • 4
    "It's a template so it'll take forever to compile." Huh? I mean technically it's not really "compiled" at all, just passed through once. When you instantiate it, it gets compiled, but no more slowly then one written manually with a specific `T`. – GManNickG Jan 05 '11 at 22:26
  • 20
    @GMan: GCC only just now (4.5) stopped having cost quadratic to the number of instantiations for template functions, and they are still drastically more expensive to parse and instantiate than manually written functions or the standard C preprocessor. The linker also has to do more work to remove duplicate instantiations. Templates also encourage #includes-in-#includes, which makes dependency calculation take longer and small (often implementation, not interface) changes to force more files to be recompiled. –  Jan 05 '11 at 22:42
  • 2
    @Joe: I agree with you, but I think you're exaggerating the cost greatly; a single function template certainly has no noticeable cost. – GManNickG Jan 05 '11 at 22:50
  • 2
    @GMan: And a single sign bit takes no time to copy, but I bet if you're reading this, you want to copy more than one sign bit, and if you're writing C++, you have more than one template function... –  Jan 05 '11 at 22:51
  • 16
    @Joe: Yes, and there's still no noticeable cost. C++ uses templates, that's just something we all have to understand, accept, and get over. – GManNickG Jan 05 '11 at 22:54
  • 4
    If you think there's "no noticeable cost" to C++'s compilation model you've never had the pleasure of working on a large C project, with linkers taking a deterministic amount of time and turnarounds measured in seconds or minutes rather than hours. (Also, you seem to be arguing there are no real caveats to my answer - maybe you should vote it up?) –  Jan 05 '11 at 22:59
  • 1
    @Joe: Again, you need to differentiate between "large projects with templates" and "the single function in my answer". Your caveat simply doesn't apply. – GManNickG Jan 05 '11 at 23:31
  • 54
    Wait, what's this "copysign is slow" business...? Using current compilers (g++ 4.6+, clang++ 3.0), `std::copysign` seems to result in _excellent_ code for me: 4 instructions (inlined), no branching, entirely using the FPU. The recipe given in this answer, by contrast, generates much worse code (many more instructions, including a multiply, moving back and forth between integer unit and FPU)... – snogglethorpe Jan 23 '12 at 06:35
  • 14
    @snogglethorpe: If you're calling `copysign` on an int it promotes to float/double, and must narrow again on return. Your compiler may optimize that promotion out but I can't find anything suggesting that's guaranteed by the standard. Also to implement signum via copysign you need to manually handle the 0 case - please make sure you include that in any performance comparison. –  Jan 23 '12 at 09:31
  • 60
    The first version is not branchless. Why do people think that a comparison used in an expression will not generate a branch? It will on most architectures. Only processors that have a cmove (or predication) will generate branchless code, but they'll do it also for ternaries or if/else if it is a win. – Patrick Schlüter Mar 12 '12 at 15:41
  • 5
    @tristopia: Can you please give me an example (on a common compiler/architecture with reasonable settings) where that generates a branch (and not because it recognized the idiom and used a more efficient branchy version)? –  Mar 12 '12 at 18:03
  • 3
    By rewriting the main expression as `return (T(0) < val) - (val < T(0));` it only requires `operator<` defined. You can also avoid having two temporaries (and constructors) by assigning `T(0)` to a temporary explicitly. The compiler _can not_ make this optimization for more complex types `T`. – orlp Mar 28 '12 at 13:01
  • 1
    @nightcracker: Good point on `operator<`. But `constexpr` functions bodies are required to have only a return statement (+ `using`/`typedef`), so you have to choose between constexpr and optimizing expensive constructors; in this case I think constexpr is more useful. –  Mar 28 '12 at 14:06
  • 1
    @user79758 not anymore on c++14. So you might think of updating your answer – Gustavo Maciel Dec 16 '14 at 07:51
  • 2
    Wut? How is subtracting `bool`s standard-compliant? – Violet Giraffe Feb 17 '15 at 07:54
  • 5
    @VioletGiraffe `bool` are implicitly convertible to `int`. `false` converts to zero and `true` converts to one. – R. Martinho Fernandes Mar 16 '15 at 15:01
  • 1
    @R.MartinhoFernandes: is it guaranteed by the standard that `true` converts to `1`, and not just to an implementation-defined non-zero value? – Violet Giraffe Mar 16 '15 at 15:05
  • 7
    @VioletGiraffe Yes, that is guaranteed. – R. Martinho Fernandes Mar 16 '15 at 15:32
  • 5
    @PatrickSchlüter: I'm confused why you think the first version needs `cmov` or predication to avoid a branch. On x86 this this can trivially be branchless with `setg`, as illustrated here: https://godbolt.org/g/XNst9Z I don't know too much about PowerPC or ARM, but from playing around on godbolt.org it appears to be branchless there also. – Josh Haberman Nov 06 '16 at 18:18
  • With C++11 can also declare constexpr for compile time generation of the sgn value where val is also a constexpr. – GameSalutes Jul 15 '17 at 15:36
  • 2
    This answer completely misses the point: it does not handle the interesting case of negative zeroes. – chqrlie Jul 15 '17 at 20:09
  • 1
    Thoughts on the best way to have this return NaN when the input is NaN? – Mr Fooz Aug 21 '17 at 16:43
  • @JoshHaberman I think you miss the point, `setg` for `cmov`, compiler would be able to optimize them if they can. https://godbolt.org/z/sEnqEE – apple apple Oct 30 '20 at 07:05
  • @appleapple: Yes, in your changed version a `cmov` is required. But your changed version is not correct (notice it's backwards: https://godbolt.org/z/Gr9sje). Also the ternary operator is not required, the solution in this answer is correct and does not require a branch or `cmov`: https://godbolt.org/z/x6r4hn). – Josh Haberman Oct 31 '20 at 21:31
  • 2
    I am confused why @PatrickSchlüter's comment has so many positive votes when it is (as far as I can see) totally incorrect. A comparison used in an expression does *not* generate a branch or a `cmov` on x86. – Josh Haberman Oct 31 '20 at 21:33
  • @JoshHaberman yes I missed that (just a quick demo anyway) / (correction: I mean `setg` *or* `cmov`) – apple apple Nov 01 '20 at 12:29
  • @JoshHaberman well, I mean if compiler can use `setg` to eliminate operator `<`, it probably would also be able to use `cmov` for similar condition (while both *doesn't* mean there is no branch) – apple apple Nov 01 '20 at 12:31
  • 5
    Ok, my comment is old (8 years) but here some precisions as my prose is far from being clear: my comment is not limited to x86 and I mixed up `cmov` and `setg` (or the equivalent for other cpus). This means that the code above is not guaranteed to be branchless on all architectures. You can check with the different godbolt links, the code is not branchless on AVR, MPS430 and when using Visual-C (even on x86). Without these precisions, my comment is indeed false and misleading. – Patrick Schlüter Nov 03 '20 at 10:34
  • 3
    `Also, some implementations here are returning a float (or T) rather than an int, which seems wasteful.` NB it's not so wasteful in a fairly typical setting in which the result of the sign function is being used immediately in a floating-point expression, so an int would be converted to a floating point value anyway. – quant_dev Mar 08 '21 at 17:39
324

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)
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 4
    Would be interesting to time it against x<0?-1:1 and 1-2*(x<0). – Mark Ransom Dec 14 '09 at 22:40
  • 9
    Mark Ransom, your expressions give incorrect results for `x==0`. – avakar Dec 14 '09 at 22:45
  • 1
    I would nominate that for the monthly *style award* badge if there was any. – Georg Fritzsche Dec 14 '09 at 22:46
  • 1
    Compilers will generally optimize simple `?:` expressions into non-branching machine code. – Tim Sylvester Dec 14 '09 at 22:52
  • 1
    Sweet. How about `(x >= 0) - (x <= 0)'? Gives -1 for negative, 1 for positive, 0 for 0. – Fred Larson Dec 14 '09 at 22:53
  • 1
    Another ternary is `x ? (x < 0) ? -1 : 1 : 0` – caf Dec 14 '09 at 22:59
  • 1
    Is a true statement guaranteed to be exactly `1` in C? – Svante Dec 14 '09 at 23:11
  • 5
    @Svante: "Each of the operators `<`, `>` ... shall yield 1 if the specified relation is true and 0 if it is false" – Stephen Canon Dec 14 '09 at 23:15
  • 11
    @Svante: not exactly. A value of `0` is "false"; any other value is "true"; however, the relational and equality operators always return `0` or `1` (see Standard 6.5.8 and 6.5.9). -- the value of the expression `a * (x == 42)` is either `0` or `a`. – pmg Dec 14 '09 at 23:21
  • Serves me right for typing off the top of my head and not reading the spec. I forgot the special case of zero, assumed it was the same as positive. For most of the places I would use a sign function, that definition would work. – Mark Ransom Dec 14 '09 at 23:26
  • Oh ... and the logical negation operator (`!`) also returns 0 or 1. `!!x` is 0 is the value of `x` is 0, 1 otherwise :) – pmg Dec 14 '09 at 23:27
  • 1
    @Svante: (still about "true" being 1) consider the `is*` functions declared in ``. 7.4.1/1 "The functions in this subclause return **nonzero (true)** if and only if ...", so not all "boolean" C functions return exclusively 0 and 1. – pmg Dec 15 '09 at 00:32
  • 22
    High-Performance Mark, I'm amazed that you missed the C++ tag. This answer is very much valid and doesn't deserve a down-vote. Moreover, I wouldn't use `copysign` for integral `x` even if I had it available. – avakar Dec 15 '09 at 08:39
  • 2
    @Matt Murrell division is typically a slow operation on cpus. – Ponkadoodle Aug 05 '11 at 05:19
  • 6
    Has anyone actually checked what code GCC/G++/any other compiler emits on a real platform? My guess is that the "branchless" version uses two branches instead of one. Bitshifting is probably a lot faster - and more portable in terms of performance. – Jørgen Fogh Sep 02 '11 at 11:29
  • @JørgenFogh http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c/18320607#comment11232656_4609795 – Mooing Duck Aug 19 '13 at 19:11
  • 2
    Awesome. Blows my mind that signum isn't standard in the library; it is in every other language that I know. – Carcigenicate Mar 17 '15 at 15:46
  • 3
    @JørgenFogh, in AVR for example you can only shift bits one by one; so any bigger bit shift is done as a loop. PAIN. – hmijail Aug 19 '15 at 11:37
  • @hmijail: Interesting. I did not know that. – Jørgen Fogh Aug 19 '15 at 14:01
  • @Joey No, it does not work without branches, `>` and `<` incur a branch! The CPU has to compare the operands, then it has to decide whether to load a `1` or a `0` into a register based on that result. Some platforms might be able to avoid that if they have a 'copy SREG flag into register' function, but that's not going to hold true for all architectures. – Pharap Aug 10 '19 at 14:45
  • 1
    @Pharap: Most architectures have a way to materialize a `0` / `1` in a register based on a compare result, with just a data dependency, not a branch (control dependency). e.g. x86 `setcc` to set a reg based on a FLAGS condition; MIPS / RISC-V `slt` (set on less-than), AArch64 CSET (built on top of CSINC with the zero register; AArch64 has a very nice small selection of branchless conditional instructions). There are a few where it's so inconvenient that branchy is preferred; e.g. [a comment on another answer](//stackoverflow.com/q/1903954#comment114329942_4609795) mentions MSP430 and AVR. – Peter Cordes Dec 30 '21 at 03:53
  • @Pharap: For some examples of how MIPS and RISC-V compare-into-register works for various conditions, using at most 2 instructions for conditions like `<=` where `slt` isn't usable alone, see [How to do less than or equal in Assembly Language(MIPS)?](https://stackoverflow.com/q/22736836). Anyway, `<` doesn't inherently incur a branch. – Peter Cordes Dec 30 '21 at 03:56
  • `(x > 0) - (x < 0)` is the best way – user16217248 Jan 18 '23 at 19:14
250

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.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
comingstorm
  • 25,557
  • 3
  • 43
  • 67
  • 77
    Upvoted this one, downvoted most popular answer. Left reeling in amazement that SO community seems to prefer a hack to use of a standard library function. May the gods of programming condemn you all to trying to decipher hacks used by clever programmers unfamiliar with language standards. Yeah, I know this is going to cost me a ton of rep on SO, but I'd rather side with comingstorm than the rest of you ... – High Performance Mark Dec 15 '09 at 07:42
  • 40
    This is close, but it gives the wrong answer for zero (according to the Wikipedia article in the question at least). Nice suggestion though. +1 anyway. – Mark Byers Dec 15 '09 at 08:25
  • 5
    If you want an integer, or if you want the exact signum result for zeros, I like Mark Byers' answer, which is fiendishly elegant! If you don't care about the above, copysign() might have a performance advanage, depending on the application -- if I were optimizing a critical loop, I would try both. – comingstorm Dec 16 '09 at 10:08
  • 11
    1) C99 is not fully supported everywhere (consider VC++); 2) this is also a C++ question. This is a good answer, but the upvoted one also works, and is more widely applicable. – Pavel Minaev Dec 31 '09 at 09:17
  • @HighPerformanceMark ~ I agree strongly with that; in addition, `copysign` seems to result in much better code than any of the suggested alternatives... – snogglethorpe Jan 23 '12 at 06:41
  • 1
    @Ólafur Waage Alternates to determine between -0.0 and 0.0: http://stackoverflow.com/questions/25332133/what-operations-and-functions-on-0-0-and-0-0-give-different-arithmetic-results – chux - Reinstate Monica Feb 18 '16 at 18:44
  • 11
    I wouldn't use `copysign()` on an AVR microcontroller, it adds an amazing 334 bytes to the program size compared to the "hacks" (if not already using anything else from `math.h`). – Torsten Römer Feb 29 '16 at 12:53
  • 6
    I'm generally for using standard library functions, but this really does not do what was requested precisely because of the note at the end about signed floating-point 0. If your use case really wants sgn(0) to give +1 or -1, then this is ok, but I think that most people looking for a sgn function are going to want that to always give 0 as that is the usual mathematical convention and it matches other languages. – Brick Mar 14 '16 at 11:57
  • 3
    @HighPerformanceMark This answer does not work for zero. Working, easy to read "hacks" are better than standard library functions that does not work (for this case) – vidstige Mar 13 '21 at 11:56
100

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, however there is copysign which can be used almost the same way via copysign(1.0, arg) and there is a true sign function 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);
Zitrax
  • 19,036
  • 20
  • 88
  • 110
Catskul
  • 17,916
  • 15
  • 84
  • 113
  • I have been wondering for the past few minutes why the standard library doesn't have sign function. It is just so common -- definitely more commonly used than gamma function which could be found in cmath header. – Taozi Feb 22 '16 at 22:33
  • 7
    The explanation I often get for similar questions is "it's easy enough to implement yourself" Which IMO is not a good reason. It completely belies the problems of where standardization, unobvious edge cases, and where to put such a widely used tool. – Catskul Feb 22 '16 at 22:35
  • I would not expect to see this marked as the answer because it says to use an external non-standard library. I don't use Boost and cannot use Boost so this is not helpful. – David Rector Jan 19 '21 at 02:34
  • 1
    @DavidRector: It also tells you that there is no such function in the standard library, which is a complete and final answer to the question. – Daniel McLaury Aug 04 '23 at 19:28
96

Apparently, the answer to the original poster's question is no. There is no standard C++ sgn function.

John
  • 5,735
  • 3
  • 46
  • 62
48

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:

OP's question 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, I 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;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    Ah, exactly what I'm after. This just changed in Pharo Smalltalk https://github.com/pharo-project/pharo/pull/1835 and I wondered if there was some sort of standard (IEC 60559 or ISO 10967) dictating behavior for negative zero and nan behaviour... I like the javascript sign https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign – aka.nice Sep 30 '18 at 19:27
30

Faster than the above solutions, including the highest rated one:

(x < 0) ? -1 : (x > 0)
xnx
  • 416
  • 4
  • 8
17

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

Tim Sylvester
  • 22,897
  • 2
  • 80
  • 94
  • 1
    If I read the link correctly that only returns -1 or 0. If you want -1, 0, or +1 then it's `sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));` or `sign = (v > 0) - (v < 0);`. – Z boson Apr 21 '15 at 07:58
  • 2
    this implies that `v` is an integer type not wider than int – phuclv Aug 10 '19 at 15:36
13

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.

ysth
  • 96,171
  • 6
  • 121
  • 214
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.

seh
  • 14,999
  • 2
  • 48
  • 58
Tabernakel
  • 131
  • 1
  • 5
5

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?

mbaitoff
  • 8,831
  • 4
  • 24
  • 32
High Performance Mark
  • 77,191
  • 7
  • 105
  • 161
  • Not standard, but may be widely available. Microsoft's starts with an underscore, which is the convention they use for non-standard extensions. Not the best choice when you're working with integers, though. – Mark Ransom Dec 14 '09 at 23:33
  • 6
    copysign is both in the ISO C (C99) and POSIX standards. See http://www.opengroup.org/onlinepubs/000095399/functions/copysign.html – lhf Dec 15 '09 at 01:19
  • 3
    What lhf said. Visual Studio is not a reference for the C standard. – Stephen Canon Dec 15 '09 at 01:42
4

The accepted answer with the overload below does indeed not trigger -Wtype-limits. But it does trigger unused argument warnings (on the is_signed variable). To avoid these the second argument should not be named like so:

template <typename T> inline constexpr
  int signum(T x, std::false_type) {
  return T(0) < x;
}

template <typename T> inline constexpr
  int signum(T x, std::true_type) {
  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 and higher an alternative could be.

template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, int>::type
inline constexpr signum(T const x) {
    return T(0) < x;  
}

template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T const x) {
    return (T(0) < x) - (x < T(0));  
}

For me it does not trigger any warnings on GCC 5.3.1.

SamVanDonut
  • 361
  • 2
  • 14
4

The question is old but there is now this kind of desired function. I added a wrapper with not, left shift and dec.

You can use a wrapper function based on signbit from C99 in order to get the exact desired behavior (see code further below).

Returns whether the sign of x is negative.
This can be also applied to infinites, NaNs and zeroes (if zero is unsigned, it is considered positive

#include <math.h>

int signValue(float a) {
    return ((!signbit(a)) << 1) - 1;
}

NB: I use operand not ("!") because the return value of signbit is not specified to be 1 (even though the examples let us think it would always be this way) but true for a negative number:

Return value
A non-zero value (true) if the sign of x is negative; and zero (false) otherwise.

Then I multiply by two with left shift (" << 1") which will give us 2 for a positive number and 0 for a negative one and finally decrement by 1 to obtain 1 and -1 for respectively positive and negative numbers as requested by OP.

Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
2

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 ) )
chattering
  • 61
  • 1
2

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);
}
Nick
  • 9,962
  • 4
  • 42
  • 80
2

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

khkarens
  • 1,305
  • 1
  • 11
  • 16
1

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 .

Serge Rogatch
  • 13,865
  • 7
  • 86
  • 158
0

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

mrclng
  • 483
  • 2
  • 14
-1
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
Gigi
  • 4,953
  • 24
  • 25
  • 3
    There are still some bad assumptions here. For example I don't believe the endianness of the float is guaranteed to be the endianness of the integer. Your check also fails on any architectures using ILP64. Really, you're just reimplementing `copysign`; if you're using `static_assert` you've got C++11, and might as well really use `copysign`. –  Mar 15 '12 at 18:24
-3
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
arrowd
  • 33,231
  • 8
  • 79
  • 110
cyberion
  • 41
  • 4
-4

Why use ternary operators and if-else when you can simply do this

#define sgn(x) x==0 ? 0 : x/abs(x)
Jagreet
  • 113
  • 1
  • 10