开发者

f(int x) { return x == 0 ? 0 : 1; } in Java without conditionals

I want to implement f(int x) { return x == 0 ? 0 : 1; } in Java.

In C, I'd just "return !!x;", but ! doesn't work li开发者_StackOverflow中文版ke that in Java. Is there some way to do it without conditionals? Without something cheesy like an unrolled version of

int ret = 0;
for (int i = 0; i < 32; i++) {
    ret |= ((x & (1 << i)) >>> i);
}

or

try {
   return x/x;
} catch (ArithmeticException e) {
   return 0;
}

)

EDIT:

So, I did a microbenchmark of three different solutions:

  1. my return x/x catch solution,
  2. the obvious x==0?0:1 solution, and
  3. Ed Staub's solution: (x|-x) >>> 31.

The timings for random int inputs (the whole int range) were:

1. 0.268716  
2. 0.324449  
3. 0.347852  

Yes, my stupid x/x solution was faster by a pretty hefty margin. Not very surprising when you consider that there are very few 0's in it, and in the vast majority of cases the fast path is taken.

The timings for the more interesting case where 50% of inputs are 0:

1. 1.256533  
2. 0.321485  
3. 0.348999  

The naive x==0?0:1 solution was faster by about 5% than the clever one (on my machine). I'll try to do some disassembly tomorrow to find out why.

EDIT2: Ok, so the disassembly for the conditional version is (excluding book-keeping):

testl rsi,rsi
setnz rax
movzbl rax,rax

The disassembly for (x|-x)>>>31 is:

movl rax,rsi
negl rax
orl rax,rsi
sarl rax,#31

I don't think anything else needs to be said.


Ok, shortest solution without conditional is probably:

return (i|-i) >>> 31;


Here is a solution:

public static int compute(int i)
{
    return ((i | (~i + 1)) >> 31) & 1; // return ((i | -i) >> 31) & 1
}

EDIT:

or you can make it more simple:

public static int compute(int i)
{
    return -(-i >> 31); // return -i >>> 31
}

EDIT2:

last solution fails with negative numbers. Take a look at @Ed Staub's solution.

EDIT3:

@Orion Adrian OK, here is a general solution:

public static int compute(int i)
{
    return (i|-i) >>> java.math.BigInteger.valueOf(Integer.MAX_VALUE).bitLength();
}


int f(int x) {
    return Math.abs(Integer.signum(x));
}

The signum() function returns the sign of the number as -1, 0 or 1. So all what's left is to turn -1 into 1, which is what abs does.


The signum function implements it this way

return (i >> 31) | (-i >>> 31);

so, just add another bitwise operation to return 0 or 1

return ((i >> 31) | (-i >>> 31)) & 1;


All of these solutions seem to suffer from the vice of taking varying degrees of effort to understand. That means the programmer who must later read and maintain this code will have to expend unnecessary effort. That costs money.

The expression

(x == 0)? 0:1

is straightforward and simple to understand. It's really the right way to do this. The use of an exception in the ordinary run of code is downright ghastly. Exceptions are for handling circumstances beyond programmer control, not for ordinary routine operations.


I wonder what the compiler would turn this into...

class kata {

    public static int f(int x){
     return -(Boolean.valueOf(x==0).compareTo(true));
    }

     public static void main(String[] args) {
         System.out.println(f(0));
         System.out.println(f(5));
         System.out.println(f(-1));

     }
}

http://ideone.com/ssAVo


This question reduces down to: "Is there a way to map boolean true,false to int 1,0 respectively without writing the conditional."

In Java, there is no standardized treatment of true as 1. The closest is use of -1. So as @Ed says, the ternary operator is as succinct as you get.


If you wanted a boolean, i think:

return x == x >>> 1

Would do it, because the only number whose set bits don't move when shifted is one with no set bits.

Under the hood, the bytecode actually uses 1 and 0 for true and false, but i don't know of any way to turn a Java language boolean value into its corresponding int value without some sort of conditional.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜