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:
- my return x/x catch solution,
- the obvious x==0?0:1 solution, and
- 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.
精彩评论