Is there a way to convert an integer to 1 if it's = = 1 without using any kind of relational operator?

In my program, I have a statement like the following: inside a loop.

y = (x >= 1)? 0:1;

      

However, I want to avoid using any kind of relational operator, because I want to use SIMD instructions and I'm not sure if relational operators work well with SIMD.

I need something like the following.

a = some_operation(x) // a will be either 1 or 0
y = 1 - a

      

Where some_operation

will convert any number equal to or greater than 1 to 1 and contain 0 to 0. So my question is, is there any some_operation

that would achieve my goal?

+3


source to share


6 answers


#define INT_BITS (CHAR_BIT * sizeof(int))

int make_zero_or_one(int x) {
   return 1 - (((x-1) >> (INT_BITS-1)) & 1);
}

      

Like the other answers suggested, it depends on the MSB being the sign bit in the ints. The function returns 0 for all int and lt = 0 and 1 otherwise. The function will fail if x-1

overflowed.



This implementation has no branches in the compiled code.

+3


source


Is there a way to convert an integer to 1 if it = = 1 without using any kind of relational operator?

For unsigned integers, you can simply:

unsigned int i = 42; // ... or any other value > 0.
unsigned int j = !!i; // j is 1 here.

i = 0;
j = !!i; // j is 0 here.

      


Update:



For signed integers, you can do

int i = ...
int j = !!(i * !((1 << ((CHAR_BITS * sizeof i) - 1)) & i)); 

      

The above line results in

  • 0

    for anyone i < 1

  • 1

    for anyone i >= 1

+3


source


Assuming you are using two addons, you can do this. (Another answer to using! X may or may not be what you are looking for, depending on the computer's instruction set and why you want to avoid relational operators)

int x = 42; // or any integer

int test = x-1;
if(test & 1 << (CHAR_BIT * sizeof(int) -1))
{
   // integer negative or zero
}
else
{
   // integer positive
}

      

+2


source


I know this answer clearly contradicts your question, but there are SIMD comparisons on all common SIMD architectures (at least everything I know).

For SSE2 parameters and int32

there is pcmpgtd

(intrinsic:) _mm_cmpgt_epi32

, assuming you have 4 integers in __m128i x

, you can write

__m128i result = _mm_cmpgt_epi32(x, _mm_setzero_si128())

      

To get -1

(i.e. 0xFFFFFFFF

) for each x>0

(i.e. x>=1

) and 0

otherwise. If you need to 1

instead -1

just write

__m128i y =  _mm_sub_epi32(_mm_setzero_si128(), result);

      

+1


source


I'm not familiar with C or with SIMD instructions, but if x is a positive integer, can't you just do it?

y = (x == 0) ? 1 : 0;

      

-1


source


Use this: y= 1/(1+e^(-10000*(x-0.995)))

This will give y = 0 for x <= 0.99

andy=1 for x>= 1

I don't know what SIMD is and there might be a better way to do it. But I figured if you don't want to use a condition, you can use a sigmoid function that returns 0 or 1 according to your criteria. This function will be yours some_operation(x)

. Note that this function will only work for numbers with 2 decimal places. That is, entering 0.99 will return 0, but returning 0.999 will return 1. Before performing the calculation, make sure you round your number down to the nearest 2-digit number.

I'll go through my thought process step by step below in case anyone is interested:

If you want to use a function, not a boolean condition, it must be continuous, which by definition would mean that the som of the values ​​will not meet the criteria. But if those values ​​were in a very narrow range, and your steps between the numbers were greater than that narrow range, it would work.

So you can use the sigmoid function. (enter it in alpha wolfram, see each change)

  y =  1/(1+e^(-x))   

      

And slide it one step to the right so that it centers around 1 instead of zero.

  y = 1/(1+e^(-(x-1)))

      

Then you can increase the slope of the function by increasing the weight.

  y= 1/(1+e^(-10000*(x-1))) 

      

Now the slope is really, really steep. But we still get y = 0.5 if we enter x = 1. So we need to move the sigmoid slightly to the left.

y= 1/(1+e^(-10000*(x-0.995))) 

      

Now we get y = 0 if x <= 0.99 and y = 1 if x> = 1. If you want to use a finer resolution, you will need to adjust the weight (in this case 10000) and the center point (in this case - 0.995). I just checked the calculation in tungsten alpha and repeated that it works. You can use weights up to 4000 if you only use 2 decimal places.

I'm sure there is a better way to do this, but I would work it out.

-1


source







All Articles