views:

390

answers:

6

I can check whether a number is odd/even using bitwise operators. Can I check whether a number is positive/zero/negative without using any conditional statements/operators like if/ternary etc.

Can the same be done using bitwise operators and some trick in C or in C++?

+4  A: 

If the high bit is set on a signed integer (byte, long, etc., but not a floating point number), that number is negative.

int x = -2300;  // assuming a 32-bit int

if ((x & 0x80000000) != 0)
{
    // number is negative
}

ADDED:

You said that you don't want to use any conditionals. I suppose you could do this:

int isNegative = (x & 0x80000000);

And at some later time you can test it with if (isNegative).

Jim Mischel
Floating-point numbers nowadays tend to be in the IEEE format, which does have an explicit sign bit.
David Thornley
@David: I didn't know that. Do you know which bit is the sign bit?
Jim Mischel
@Jim Mischel: according to this page: http://en.wikipedia.org/wiki/Double_precision_floating-point_format, it is also the high bit.
Evan Teran
It's the highest bit for IEEE 754 floats also. `1 << sizeof(yourfloat)*8-1` should get you the mask to use in any case.
Matt Kane
Jim Mischel
Will this logic change for little endian vs big endian machines?
Chubsdad
@Chubsdad: no. Big/little endian is strictly about the order of bytes in memory. When computation is done, a value is treated as a single unit and the sign bit is always the high bit. (On modern processors.)
Jim Mischel
+8  A: 

Can I check whether a number is positive/zero/negative without using any conditional statements/operators like if/ternary etc.

Of course:

bool is_positive = number > 0;
bool is_negative = number < 0;
bool is_zero = number == 0;
Konrad Rudolph
Well, I think he is asking about bitwise operators...
Petar Minchev
Actually this is the only platform independent way. Moreover, it will be more efficient than the bitwise check on **all** platforms.
ybungalobill
+1  A: 

Signed integers and floating points normally use the most significant bit for storing the sign so if you know the size you could extract the info from the most significant bit.

There is generally little benefit in doing this this since some sort of comparison will need to be made to use this information and it is just as easy for a processor to tests whether something is negative as it is to test whether it is not zero. If fact on ARM processors, checking the most significant bit will be normally MORE expensive than checking whether it is negative up front.

doron
A: 

You can differentiate between negative/non-negative by looking at the most significant bit. In all representations for signed integers, that bit will be set to 1 if the number is negative.

There is no test to differentiate between zero and positive, except for a direct test against 0.

To test for negative, you could use

#define IS_NEGATIVE(x) ((x) & (1U << ((sizeof(x)*CHAR_BIT)-1)))
Bart van Ingen Schenau
@Bart: The size and the width of an integer type are not strictly related in the sense that you are suggesting. Second the most significant bit of the unsigned type is not necessarily the sign bit: the width of the corresponding signed type might be smaller, or it might even be one more than that of the unsigned type.
Jens Gustedt
@Jens: Pedantically speaking, you are right and is it impossible to determine which bit contains the sign. But can you name a platform in existence where this code does not work?
Bart van Ingen Schenau
@Bart: They are rare, I admit, but exist. Unfortunately in German, but you might get the idea: http://www.schellong.de/c_padding_bits.htm. Also in this discussion there is an interesting example: http://www.rhinocerus.net/forum/lang-c/2007-padding-bits.html
Jens Gustedt
+4  A: 

There is a detailed discussion on the Bit Twiddling Hacks page.

int v;      // we want to find the sign of v
int sign;   // the result goes here 

// CHAR_BIT is the number of bits per byte (normally 8).
sign = -(v < 0);  // if v < 0 then -1, else 0. 
// or, to avoid branching on CPUs with flag registers (IA32):
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
// or, for one less instruction (but not portable):
sign = v >> (sizeof(int) * CHAR_BIT - 1); 

// The last expression above evaluates to sign = v >> 31 for 32-bit integers.
// This is one operation faster than the obvious way, sign = -(v < 0). This
// trick works because when signed integers are shifted right, the value of the
// far left bit is copied to the other bits. The far left bit is 1 when the value
// is negative and 0 otherwise; all 1 bits gives -1. Unfortunately, this behavior
// is architecture-specific.

// Alternatively, if you prefer the result be either -1 or +1, then use:

sign = +1 | (v >> (sizeof(int) * CHAR_BIT - 1));  // if v < 0 then -1, else +1

// On the other hand, if you prefer the result be either -1, 0, or +1, then use:

sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
// Or, for more speed but less portability:
sign = (v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1));  // -1, 0, or +1
// Or, for portability, brevity, and (perhaps) speed:
sign = (v > 0) - (v < 0); // -1, 0, or +1

// If instead you want to know if something is non-negative, resulting in +1
// or else 0, then use:

sign = 1 ^ ((unsigned int)v >> (sizeof(int) * CHAR_BIT - 1)); // if v < 0 then 0, else 1

// Caveat: On March 7, 2003, Angus Duggan pointed out that the 1989 ANSI C
// specification leaves the result of signed right-shift implementation-defined,
// so on some systems this hack might not work. For greater portability, Toby
// Speight suggested on September 28, 2005 that CHAR_BIT be used here and
// throughout rather than assuming bytes were 8 bits long. Angus recommended
// the more portable versions above, involving casting on March 4, 2006.
// Rohit Garg suggested the version for non-negative integers on September 12, 2009. 
grokus
A: 

This can not be done in a portable way with bit operations in C. The representations for signed integer types that the standard allows can be much weirder than you might suspect. In particular the value with sign bit on and otherwise zero need not be a permissible value for the signed type nor the unsigned type, but a so-called trap representation for both types.

All computations with bit operators that you can thus do might have a result that leads to undefined behavior.


In any case as some of the other answers suggest, this is not really necessary and comparison with < or > should suffice in any practical context, is more efficient, easier to read... so just do it that way.

Jens Gustedt