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

21

517

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.

Silverts answered 14/12, 2009 at 22:27 Comment(12)
What should it return for 0?Fancie
@Craig McQueen; that depends on if it is a positive zero or negative zero.Redroot
I noticed that you specified the return value as an integer. Are you looking for a solution that takes integers or floating point numbers?Felicitasfelicitate
@ysth: True for floats but not for ints, so I suppose then the question is: is the questioner interested in floats or ints?Fancie
@Redroot @Craig McQueen, false for floats too, no? sgn(x)'s definition says to return 0 if x==0. According to IEEE 754, negative zero and positive zero should compare as equal.Barbellate
@RJFalconer: I don't understand your comment. what are you saying is false?Redroot
@Redroot "it depends on positive zero or negative zero". In fact, it does not.Barbellate
@Barbellate The sgn() of your comment 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. It depends on user's need. Also NaN is a consideration as OP specified float.Rathbun
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. @Barbellate In the relatively few cases that signed zeros matter, you get a sensible answer, and in the other cases it makes no difference.Bottali
What result should be returned for a NaN value?Berke
C or C++ - pick one, not both. They are different languages.Joashus
C99, C++ : cplusplus.com/reference/cmath/signbitJerlenejermain
H
629

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

Hearth answered 5/1, 2011 at 22:19 Comment(29)
"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.Bandmaster
@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.Hearth
@Joe: I agree with you, but I think you're exaggerating the cost greatly; a single function template certainly has no noticeable cost.Bandmaster
@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...Hearth
@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.Bandmaster
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?)Hearth
@Joe: Again, you need to differentiate between "large projects with templates" and "the single function in my answer". Your caveat simply doesn't apply.Bandmaster
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)...Fullback
@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.Hearth
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.Jovitajovitah
@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)?Hearth
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.Basketwork
@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.Hearth
@Hearth not anymore on c++14. So you might think of updating your answerSnelling
Wut? How is subtracting bools standard-compliant?Spirituous
@VioletGiraffe bool are implicitly convertible to int. false converts to zero and true converts to one.Lodger
@R.MartinhoFernandes: is it guaranteed by the standard that true converts to 1, and not just to an implementation-defined non-zero value?Spirituous
@VioletGiraffe Yes, that is guaranteed.Lodger
@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: 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.Criollo
With C++11 can also declare constexpr for compile time generation of the sgn value where val is also a constexpr.Heraclitus
This answer completely misses the point: it does not handle the interesting case of negative zeroes.Afternoons
Thoughts on the best way to have this return NaN when the input is NaN?Akerley
@JoshHaberman I think you miss the point, setg for cmov, compiler would be able to optimize them if they can. godbolt.org/z/sEnqEEBiological
@appleapple: Yes, in your changed version a cmov is required. But your changed version is not correct (notice it's backwards: 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: godbolt.org/z/x6r4hn).Criollo
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.Criollo
@JoshHaberman yes I missed that (just a quick demo anyway) / (correction: I mean setg or cmov)Biological
@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)Biological
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.Jovitajovitah
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.Misdirection
F
327

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)
Felicitasfelicitate answered 14/12, 2009 at 22:30 Comment(23)
Would be interesting to time it against x<0?-1:1 and 1-2*(x<0).Flanigan
Mark Ransom, your expressions give incorrect results for x==0.Homage
I would nominate that for the monthly style award badge if there was any.Stepheniestephens
Compilers will generally optimize simple ?: expressions into non-branching machine code.Dualistic
Sweet. How about `(x >= 0) - (x <= 0)'? Gives -1 for negative, 1 for positive, 0 for 0.Eulogia
Another ternary is x ? (x < 0) ? -1 : 1 : 0Belligerence
Is a true statement guaranteed to be exactly 1 in C?Chief
@Svante: "Each of the operators <, > ... shall yield 1 if the specified relation is true and 0 if it is false"Granule
@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.Elusion
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.Flanigan
Oh ... and the logical negation operator (!) also returns 0 or 1. !!x is 0 is the value of x is 0, 1 otherwise :)Elusion
@Svante: (still about "true" being 1) consider the is* functions declared in <ctype.h>. 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.Elusion
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.Homage
@Matt Murrell division is typically a slow operation on cpus.Stearin
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.Bandler
@JørgenFogh #1904454Autobiographical
Awesome. Blows my mind that signum isn't standard in the library; it is in every other language that I know.Actin
@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.Dunk
@hmijail: Interesting. I did not know that.Bandler
@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.Joashus
@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 mentions MSP430 and AVR.Cadre
@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)?. Anyway, < doesn't inherently incur a branch.Cadre
(x > 0) - (x < 0) is the best wayBielefeld
M
253

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.

Majestic answered 15/12, 2009 at 4:19 Comment(9)
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 ...Sandell
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.Felicitasfelicitate
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.Majestic
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.Deutoplasm
@HighPerformanceMark ~ I agree strongly with that; in addition, copysign seems to result in much better code than any of the suggested alternatives...Fullback
@Ólafur Waage Alternates to determine between -0.0 and 0.0: #25332633Rathbun
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).Tolidine
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.Bottali
@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)Weaponeer
M
102

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);
Martyry answered 1/6, 2013 at 4:23 Comment(4)
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.Harm
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.Martyry
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.Mesopotamia
@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.Luckless
J
99

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

Jerald answered 13/4, 2012 at 0:24 Comment(0)
R
49

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 x cases: 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 as -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;
}
Rathbun answered 18/2, 2016 at 20:24 Comment(1)
Ah, exactly what I'm after. This just changed in Pharo Smalltalk 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 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Definition
R
30

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

(x < 0) ? -1 : (x > 0)
Rafaello answered 17/8, 2011 at 20:10 Comment(6)
What type is x? Or are you using a #define?Flameproof
Your type is not faster. It will cause a cache miss quite often.Palladio
Cache miss? I'm not sure how. Perhaps you meant branch misprediction?Martyry
It seems to me this will result in a warning of confusing integer and boolean types!Chibouk
how this will be fast with the branch?Allhallows
What's wrong with (x < 0) ? -1 : (x > 0) ? 1 : 0? It might be less terse, but it's more readable.Joashus
D
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...

Dualistic answered 14/12, 2009 at 22:52 Comment(2)
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);.Lexine
this implies that v is an integer type not wider than intAngelicangelica
R
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.

Redroot answered 15/12, 2009 at 5:59 Comment(3)
There's many mathematical applications in which the sign(x) is necessary. Otherwise I'd just do if (x < 0).Flameproof
It looks like this function takes a float as parameter... I wouldn't want to pay the conversion price just to get the sign bit...Fleenor
correct. but this question is about floatsRedroot
M
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.

Modulator answered 26/5, 2015 at 3:5 Comment(0)
S
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?

Sandell answered 14/12, 2009 at 23:15 Comment(3)
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.Flanigan
copysign is both in the ISO C (C99) and POSIX standards. See opengroup.org/onlinepubs/000095399/functions/copysign.htmlWetterhorn
What lhf said. Visual Studio is not a reference for the C standard.Granule
C
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.

Celaeno answered 13/1, 2017 at 15:59 Comment(2)
To avoid the -Wunused-parameter warning just use unnamed parameters.Mala
That is actually very true. I missed that. However, I like the C++11 alternative more either way.Celaeno
I
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.

Intensifier answered 17/4, 2020 at 8:57 Comment(2)
0 will be positive then too... which might or might not be what OP wanted...Buchenwald
well we may never know what OP truly wanted if n=0... !Intensifier
C
3

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

Centralia answered 29/8, 2018 at 7:58 Comment(2)
Note that this was suggested before: https://mcmap.net/q/23123/-is-there-a-standard-sign-function-signum-sgn-in-c-c.Pegpega
Boost is not a standard library and some of us are not allowed to use Boost for our projects.Mesopotamia
F
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 ) )
Fibrin answered 10/7, 2014 at 23:35 Comment(4)
One should prefer templates over macros in C++.Lungfish
In C, there is no template ...... helloacm.com/how-to-implement-the-sgn-function-in-cSushi
I thought this was a good answer then I looked at my own code and found this: #define sign(x) (((x) > 0) - ((x) < 0)) which is good too.Cestar
an inline function is better than a macro in C, and in C++ template is betterAngelicangelica
A
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);
}
Allhallows answered 15/7, 2017 at 20:3 Comment(0)
B
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 .

Brach answered 19/11, 2018 at 9:29 Comment(0)
D
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...)

Dissemble answered 11/8, 2017 at 8:19 Comment(0)
B
-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
Behemoth answered 3/2, 2012 at 1:4 Comment(1)
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.Hearth
C
-3
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
Crippen answered 4/11, 2012 at 15:46 Comment(0)
S
-4

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

#define sgn(x) x==0 ? 0 : x/abs(x)
Subjunctive answered 30/6, 2018 at 1:29 Comment(4)
Your definition uses a ternary operator as well.Pegpega
Yes Definitely, but it just uses one ternary operator to separate zero and non-zero numbers. Others' versions include nested ternary ops to separate positive, negative and zero.Subjunctive
Using an integer division is very inefficient and abs() is only for integers.Cestar
Undefined behavior possible when x == INT_MIN.Rathbun

© 2022 - 2024 — McMap. All rights reserved.