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.
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.)
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)
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.
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);
Apparently, the answer to the original poster's question is no. There is no standard C++ sgn function.
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;
}
Faster than the above solutions, including the highest rated one:
(x < 0) ? -1 : (x > 0)
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...
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.
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.
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?
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.
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.
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 ) )
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);
}
You can use boost::math::sign() method from boost/math/special_functions/sign.hpp if boost is available.
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 .
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...)
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
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
Why use ternary operators and if-else when you can simply do this
#define sgn(x) x==0 ? 0 : x/abs(x)
Related
I need a function which can take string as an input and generate hash code out of it. Currently, in c++ we have std::hash to do this but this returns the hash code of type size_t( unsigned long long ). Here, I need a hash function which can give me the hash code of type signed long long.
I have also tried using the modulus operator but that gives me negative values and those are not reliable. Hence, pls advise me on the hash function I can use in C++ so that I get hash code of type signed long long.
I need a hash function which can give me the hash code of type signed long long.
You could just set to 0 the most significant bit, unless your architecture have a weird internal representation of integral types, this would produce a positive number when converted to signed type of the same size.
template <class S>
constexpr size_t clamp_to_positive(size_t value)
{
return value & (std::numeric_limits<size_t>::max() >>
(std::numeric_limits<size_t>::digits - std::numeric_limits<S>::digits)
);
}
You can then call it as
auto my_hash = clamp_to_positive<long long>(std::hash<std::string>{}(source_string));
As noted by Ben Voigt, though, the easiest way is to just right-shift by one the unsigned value.
auto my_hash = static_cast<long long>(std::hash<std::string>{}(source_string) >> 1);
Another way to tackle this problem is to force the modulo operation to always return a positive value.
// Evaluates abs(x) as an unsigned type, avoiding corner case overflow.
template <class T>
constexpr auto unsigned_abs(T x)
{
static_assert(std::is_integral_v<T>);
if constexpr ( std::is_unsigned_v<T> ) {
return x;
}
return x < 0
? ~static_cast<std::make_unsigned_t<T>>(x) + 1
: static_cast<std::make_unsigned_t<T>>(x);
}
// Evaluates abs(x) % abs(y) avoiding overflows. The result has the same type
// of y and it's always 0 <= result < y. It has UB when y == 0.
template <class X, class Y>
auto absolute_remainder(X x, Y y)
{
static_assert( std::is_integral_v<X> && std::is_integral_v<Y> );
return static_cast<Y>(unsigned_abs(x) % unsigned_abs(y));
}
Whether you really need one of those or maybe a change in the current design of your program is left to you to figure out.
I need to convert normalized integer values to and from real floating-point values. For instance, for int16_t, a value of 1.0 is represented by 32767 and -1.0 is represented by -32768. Although it's a bit tedious to do this for each integer type, both signed and unsigned, it's still easy enough to write by hand.
However, I want to use standard methods whenever possible rather than going off and reinventing the wheel, so what I'm looking for is something like a standard C or C++ header, a Boost library, or some other small, portable, easily-incorporated source that already performs these conversions.
Here's a templated solution using std::numeric_limits:
#include <cstdint>
#include <limits>
template <typename T>
constexpr double normalize (T value) {
return value < 0
? -static_cast<double>(value) / std::numeric_limits<T>::min()
: static_cast<double>(value) / std::numeric_limits<T>::max()
;
}
int main () {
// Test cases evaluated at compile time.
static_assert(normalize(int16_t(32767)) == 1, "");
static_assert(normalize(int16_t(0)) == 0, "");
static_assert(normalize(int16_t(-32768)) == -1, "");
static_assert(normalize(int16_t(-16384)) == -0.5, "");
static_assert(normalize(uint16_t(65535)) == 1, "");
static_assert(normalize(uint16_t(0)) == 0, "");
}
This handles both signed and unsigned integers, and 0 does normalize to 0.
View Successful Compilation Result
I'd question whether your intent is correct here (or indeed that of most of the answers).
Since you're likely just dealing with something like an integer representation of a "real" value such as that produced by an ADC - I'd argue that in fact a floating point fraction of +32767/32768 (not +1.0) is represented by the integer +32767, as a value of +1.0 can't actually be expressed in this form due to the 2's complement arithmetic used.
Although it's a bit tedious to do this for each integer type, both
signed and unsigned, it's still easy enough to write by hand.
You certainly don't need to do this for each integer type! Use <limits> instead.
template<class T> double AsDouble(const T x) {
const double valMin = std::numeric_limits<T>::min();
const double valMax = std::numeric_limits<T>::max();
return 2 * (x - valMin) / (valMax - valMin) - 1; // note: 0 does not become 0.
}
I stumbled upon this code to check for NaN:
/**
* isnan(val) returns true if val is nan.
* We cannot rely on std::isnan or x!=x, because GCC may wrongly optimize it
* away when compiling with -ffast-math (default in RASR).
* This function basically does 3 things:
* - ignore the sign (first bit is dropped with <<1)
* - interpret val as an unsigned integer (union)
* - compares val to the nan-bitmask (ones in the exponent, non-zero significand)
**/
template<typename T>
inline bool isnan(T val) {
if (sizeof(val) == 4) {
union { f32 f; u32 x; } u = { (f32)val };
return (u.x << 1) > 0xff000000u;
} else if (sizeof(val) == 8) {
union { f64 f; u64 x; } u = { (f64)val };
return (u.x << 1) > 0x7ff0000000000000u;
} else {
std::cerr << "isnan is not implemented for sizeof(datatype)=="
<< sizeof(val) << std::endl;
}
}
This looks arch dependent, right? However, I'm not sure about endianess, because no matter about little or big endian, the float and the int are probably stored in the same order.
Also, I wonder whether something like
volatile T x = val;
return std::isnan(x);
would have worked.
This was used with GCC 4.6 in the past.
Also, I wonder whether something like std::isnan((volatile)x) would have worked.
isnan takes its argument by value so the volatile qualifier would have been discarded. In other words, no, this doesn’t work.
The code you’ve posted relies on a specific floating point representation (IEEE). It also exhibits undefined behaviour since it relies on the union hack to retrieve the underlying float representation.
On a note about code review, the function is badly written even if we ignore the potential problems of the previous paragraph (which are justifiable): why does the function use runtime checks rather than compile-time checks and compile time error handling? It would have been better and easier just to offer two overloads.
Going through some code, I found this :
#ifdef trunc
# undef trunc
#endif
inline float trunc(float x)
{
return (x < 0.0f) ? float(int(x)) : float(int(x));
}
inline double trunc(double x)
{
return (x < 0.0f) ? double(int(x)) : double(int(x));
}
inline long double trunc(long double x)
{
return (x < 0.0f) ? (long double)(int(x)) : (long double)(int(x));
}
#endif // _WIN32
Of course, the ?: operator always returns one and the same value in each case, regardless of its conditional expression.
On the other hand, I guess the author had his reasons to write these functions this way; I can't find one though. Any idea ? Is this just an error (typo) ?
[EDIT] Reply from the author :
Good point - this is just overzealous cut-and-paste from the definition of
round(). The following should be just fine (other than the limitation on the
range of int):
inline float trunc(float x)
{
return float(int(x));
}
inline double trunc(double x)
{
return double(int(x));
}
inline long double trunc(long double x)
{
return (long double)(int(x));
}
This code looks wrong.
My guess is that they meant something more like this:
inline float trunc(float x)
{
return (x < 0.0f) ? -float(int(-x)) : float(int(x));
}
But even that's dubious. I believe int(x) always performs truncation, so even then the two branches of ?: should yield the same result.
In case rounding mode does matter to the typecast (and after a moment's thought, I'm not sure it does), you may really want to use a function like modf, modff or modfl to break the number into integer and fractional portions, and discard the fractional portion.
For example:
inline float trunc(float x)
{
float int_part;
modf(x, &int_part);
return int_part;
}
Edit: One other observation. The original code will fail for values that do not fit in an int. Yet another strike against it.
The code returns the same output for both the conditions. This is just a redundancy and moreover, the float(int(x)) doesn't make a point. Because int(x) converts the number to an integer, again converting it to float or double makes no difference but only the type of the variable returned.
I'm trying to write a branchless function to return the MAX or MIN of two integers without resorting to if (or ?:). Using the usual technique I can do this easily enough for a given word size:
inline int32 imax( int32 a, int32 b )
{
// signed for arithmetic shift
int32 mask = a - b;
// mask < 0 means MSB is 1.
return a + ( ( b - a ) & ( mask >> 31 ) );
}
Now, assuming arguendo that I really am writing the kind of application on the kind of in-order processor where this is necessary, my question is whether there is a way to use C++ templates to generalize this to all sizes of int.
The >>31 step only works for int32s, of course, and while I could copy out overloads on the function for int8, int16, and int64, it seems like I should use a template function instead. But how do I get the size of a template argument in bits?
Is there a better way to do it than this? Can I force the mask T to be signed? If T is unsigned the mask-shift step won't work (because it'll be a logical rather than arithmetic shift).
template< typename T >
inline T imax( T a, T b )
{
// how can I force this T to be signed?
T mask = a - b;
// I hope the compiler turns the math below into an immediate constant!
mask = mask >> ( (sizeof(T) * 8) - 1 );
return a + ( ( b - a ) & mask );
}
And, having done the above, can I prevent it from being used for anything but an integer type (eg, no floats or classes)?
EDIT: This answer is from before C++11. Since then, C++11 and later has offered make_signed<T> and much more as part of the standard library
Generally, looks good, but for 100% portability, replace that 8 with CHAR_BIT (or numeric_limits<char>::max()) since it isn't guaranteed that characters are 8-bit.
Any good compiler will be smart enough to merge all of the math constants at compile time.
You can force it to be signed by using a type traits library. which would usually look something like (assuming your numeric_traits library is called numeric_traits):
typename numeric_traits<T>::signed_type x;
An example of a manually rolled numeric_traits header could look like this: http://rafb.net/p/Re7kq478.html (there is plenty of room for additions, but you get the idea).
or better yet, use boost:
typename boost::make_signed<T>::type x;
EDIT: IIRC, signed right shifts don't have to be arithmetic. It is common, and certainly the case with every compiler I've used. But I believe that the standard leaves it up the compiler whether right shifts are arithmetic or not on signed types. In my copy of the draft standard, the following is written:
The value of E1 >> E2 is E1
rightshifted E2 bit positions. If E1
has an unsigned type or if E1 has a
signed type and a nonnegative value,
the value of the result is the
integral part of the quotient of E1
divided by the quantity 2 raised to
the power E2. If E1 has a signed type
and a negative value, the resulting
value is implementation defined.
But as I said, it will work on every compiler I've seen :-p.
Here's another approach for branchless max and min. What's nice about it is that it doesn't use any bit tricks and you don't have to know anything about the type.
template <typename T>
inline T imax (T a, T b)
{
return (a > b) * a + (a <= b) * b;
}
template <typename T>
inline T imin (T a, T b)
{
return (a > b) * b + (a <= b) * a;
}
tl;dr
To achieve your goals, you're best off just writing this:
template<typename T> T max(T a, T b) { return (a > b) ? a : b; }
Long version
I implemented both the "naive" implementation of max() as well as your branchless implementation. Both of them were not templated, and I instead used int32 just to keep things simple, and as far as I can tell, not only did Visual Studio 2017 make the naive implementation branchless, it also produced fewer instructions.
Here is the relevant Godbolt (and please, check the implementation to make sure I did it right). Note that I'm compiling with /O2 optimizations.
Admittedly, my assembly-fu isn't all that great, so while NaiveMax() had 5 fewer instructions and no apparent branching (and inlining I'm honestly not sure what's happening) I wanted to run a test case to definitively show whether the naive implementation was faster or not.
So I built a test. Here's the code I ran. Visual Studio 2017 (15.8.7) with "default" Release compiler options.
#include <iostream>
#include <chrono>
using int32 = long;
using uint32 = unsigned long;
constexpr int32 NaiveMax(int32 a, int32 b)
{
return (a > b) ? a : b;
}
constexpr int32 FastMax(int32 a, int32 b)
{
int32 mask = a - b;
mask = mask >> ((sizeof(int32) * 8) - 1);
return a + ((b - a) & mask);
}
int main()
{
int32 resInts[1000] = {};
int32 lotsOfInts[1'000];
for (uint32 i = 0; i < 1000; i++)
{
lotsOfInts[i] = rand();
}
auto naiveTime = [&]() -> auto
{
auto start = std::chrono::high_resolution_clock::now();
for (uint32 i = 1; i < 1'000'000; i++)
{
const auto index = i % 1000;
const auto lastIndex = (i - 1) % 1000;
resInts[lastIndex] = NaiveMax(lotsOfInts[lastIndex], lotsOfInts[index]);
}
auto finish = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start).count();
}();
auto fastTime = [&]() -> auto
{
auto start = std::chrono::high_resolution_clock::now();
for (uint32 i = 1; i < 1'000'000; i++)
{
const auto index = i % 1000;
const auto lastIndex = (i - 1) % 1000;
resInts[lastIndex] = FastMax(lotsOfInts[lastIndex], lotsOfInts[index]);
}
auto finish = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start).count();
}();
std::cout << "Naive Time: " << naiveTime << std::endl;
std::cout << "Fast Time: " << fastTime << std::endl;
getchar();
return 0;
}
And here's the output I get on my machine:
Naive Time: 2330174
Fast Time: 2492246
I've run it several times getting similar results. Just to be safe, I also changed the order in which I conduct the tests, just in case it's the result of a core ramping up in speed, skewing the results. In all cases, I get similar results to the above.
Of course, depending on your compiler or platform, these numbers may all be different. It's worth testing yourself.
The Answer
In brief, it would seem that the best way to write a branchless templated max() function is probably to keep it simple:
template<typename T> T max(T a, T b) { return (a > b) ? a : b; }
There are additional upsides to the naive method:
It works for unsigned types.
It even works for floating types.
It expresses exactly what you intend, rather than needing to comment up your code describing what the bit-twiddling is doing.
It is a well known and recognizable pattern, so most compilers will know exactly how to optimize it, making it more portable. (This is a gut hunch of mine, only backed up by personal experience of compilers surprising me a lot. I'll be willing to admit I'm wrong here.)
You may want to look at the Boost.TypeTraits library. For detecting whether a type is signed you can use the is_signed trait. You can also look into enable_if/disable_if for removing overloads for certain types.
I don't know what are the exact conditions for this bit mask trick to work but you can do something like
#include<type_traits>
template<typename T, typename = std::enable_if_t<std::is_integral<T>{}> >
inline T imax( T a, T b )
{
...
}
Other useful candidates are std::is_[un]signed, std::is_fundamental, etc. https://en.cppreference.com/w/cpp/types
In addition to tloch14's answer "tl;dr", one can also use an index into an array. This avoids the unwieldly bitshuffling of the "branchless min/max"; it's also generalizable to all types.
template<typename T> constexpr T OtherFastMax(const T &a, const T &b)
{
const T (&p)[2] = {a, b};
return p[a>b];
}