10

An interview question I was asked last week:

I need a function to print out whether a number is positive or negative without using conditional statements like if else while for switch a? b:c etc. How can i do that.

I told the interviewer that it's impossible cuz the question is 'conditional' in nature. He told me it's possible but didn't tell me how. I did quite a bit of search but no good answers.

OMGPOP
  • 1,995
  • 8
  • 52
  • 95
  • 37
    Correct answer- "It doesn't matter because the question is stupid. Anyone using tricks in production code instead of using an if statement deserves to be fired." – Gabe Sechan Jun 29 '14 at 05:32
  • 4
    What's the point of such questions in interviews? I doubt the employer finds the best candidate with this. Sure, thinking is stretched but the solution is less than readable. – bbalchev Jun 29 '14 at 05:33
  • I hope this was for a job close to bits or hardware. – David Ehrmann Jun 29 '14 at 06:47
  • @bbalchev to see if candidates call out the interviewer on a bad question? – David Ehrmann Jun 29 '14 at 06:49
  • @GabeSechan sure.. that's the correct answer if you don't want the job. – harold Jun 29 '14 at 10:09
  • 3
    @GabeSechan If this is a good idea depends on the nature of the code. Such tricks can be useful in high performance code. And in cryptography code they are essential to avoid side channel attacks. Many modern crypto libraries mandate that there must not be any branches depending on secret data. So I strongly disagree with your statement in such a general form. – CodesInChaos Jun 29 '14 at 10:40
  • 1
    `a ? b : c` is not a conditional statement, it's a conditional expression. In fact, being an expression, not a statement, is the whole raison d'être for the conditional operator in the first place! Therefore, the question is wrong ;-) (Hey, if you want to play tricks with your interviewees, get your frigging questions right!) – Jörg W Mittag Jun 29 '14 at 10:47
  • This (and other useful and for some reason beloved by interviewers everywhere) number manipulations can be found by googling for [bit manipulation tricks](https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign). The answer to this question is the first one on the list. – blgt Jun 29 '14 at 17:03
  • Can't you do something like `return (a-0)>0;` – Tallmaris Jun 29 '14 at 17:17
  • "*What's the point of such questions in interviews?*" - They try to find out how good your problem-solving skills/abilities are. Gabe is correct, but that's not the point here. – JensG Jun 29 '14 at 23:58

8 Answers8

20

One possible solution:

String[] responses = {"Positive", "Negative"};
System.out.println(responses[(i >> 31) & 1]);

This also counts zero as a positive number.

Because integers in Java are required to be stored in two's complement (or behave as if they are) the highest bit of any negative number is 1, and the highest bit of any other number is 0. (i >> 31) copies the highest bit to every other bit (so negative numbers become 11111111 11111111 11111111 11111111 and positive/zero numbers become 00000000 00000000 00000000 00000000). & 1 sets all but the lowest bit to 0. The combination (i >> 31) & 1 effectively reads only the highest bit of i.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • Yes. But my explanation might be confusing. – user253751 Jun 29 '14 at 05:36
  • If we're going to count jump tables, you could say ```switch (i / i) { case -1: ...; break; case 1: ...; break; }``` and hope the compiler agrees with you about making it a jump table. – David Ehrmann Jun 29 '14 at 06:44
  • 1
    The question didn't specify only integers. You could get around that with casting `i/abs(i)` to `int32` before doing the above. – ebarr Jun 29 '14 at 08:01
  • 1
    @ebarr you can use `Double.doubleToLongBits` or `Float.floatToIntBits` and then the same thing - floats and doubles also use the highest bit for sign. – user253751 Jun 29 '14 at 09:16
  • 1
    Shifting to the right 31 bits moves the 'sign bit' to the lowest-order bit; 'anding' that with 1 will produce either 1 or 0. Now you have the index into the array of strings. I think >>> would shift it without extending the sign bit, and eliminate the need for the and, so that another answer would be to use `responses[i >>> 31)]`. – arcy Jun 29 '14 at 17:26
  • @immibis with `Double.doubleToLongBits` wouldn't you need to shift by 63 bits to get the signing bit? – ebarr Jun 30 '14 at 03:11
6

Just to elaborate on immibis' answer a bit:

int index(int i) {
    return 1 + (i>>31) - (-i>>31);
}

String[] text = {"negative", "zero", "positive"};

private String text(int i) {
    return text[index(i)];
}

The signed shift i>>31 converts every negative number into -1 and every other into 0. Computing -i>>31 allows to tell positive numbers from non-positive ones. Now look at the computed index:

positive: 1 +    0 - (-1) = 2
zero:     1 +    0 -    0 = 1
negative: 1 + (-1) -    0 = 0
maaartinus
  • 44,714
  • 32
  • 161
  • 320
5

Here is a variation, accounting for the fact that zero is neither positive or negative:

    int x = (int)Math.sqrt(Math.pow(n, 2));
    try {
        x = n / x;
    }
    catch (ArithmeticException e) {
        x = 0;
    }

    String[] result = {"negative", "zero", "positive"};
    System.out.println(result[x + 1]);
Wcrousse
  • 203
  • 2
  • 10
2

Super simple solution abusing the fact that arrays cannot have a negative size:

void printPositive(int i) {
    try { new int[i]; System.out.println("positive"); } 
    catch( NegativeArraySizeException e) { System.out.println("negative"); }
}

Okay, this answer may allocate a huge array if i is positive and the VM might use conditionals under its hood when evaluating new int[i], but at least it would show the interviewer some kind of creativity. In addition, it might show the interviewer that you can think out of the box (because he might anticipate that you will do some bit magic as most of the other answers use) and do something completely different.

gexicide
  • 38,535
  • 21
  • 92
  • 152
  • It would *always* fail for number close to `Integer.MAX_VALUE` as the [maximum array size](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/ArrayList.java/#229) is a bit smaller (the JVM needs some place for a header). Note also, that avoiding branches has a good reason: branches are slow (and most answers to this question are even slower). – maaartinus Jun 29 '14 at 18:35
  • @maaartinus: Right, we should catch `OutOfMemoryError`, too. Of course, this solution is nothing one would use for real code. Branches are not slow by default; only branches that cannot be predicted well are slow. So if the input to your function is positive 95% of the time, the branch will be virtually free for positive inputs. – gexicide Jun 30 '14 at 07:53
  • Concerning branches, I [know](http://stackoverflow.com/questions/19689214/strange-branching-performance). And there's also the [(in)famous question](http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array). – maaartinus Jun 30 '14 at 08:11
1

Old answer. The reason I am making this new answer is because I was using Boolean's compareTo method, which uses a ternary operator to convert boolean expressions to binary.

Here is my new answer, which is much more unreadable.

public static String positiveOrNegative(int n) {

    ArrayList<String> responses = new ArrayList<String>();
    // first element should be "Zero", so if n is 0, the response is "Zero"
    responses.add("Zero");

    // this populates the ArrayList with elements "Positive" for n elements
    // so that if n is positive, n will be an index in the ArrayList
    // and the return will be "Positive"
    // but still if n is negative, it will never be an index in the ArrayList
    for (int i = 0; i < n; i++) {
        responses.add("Positive");
    }

    String response = "";
    try {
        // try to get a response from the ArrayList
        response = responses.get(n);
    } catch (Exception e) {
         // index is out of bounds, so it must have been negative
        response = "Negative";
    }

    return response;
}

public static void main(String[] args) {
    System.out.println(positiveOrNegative(4)); // Positive
    System.out.println(positiveOrNegative(1)); // Positive
    System.out.println(positiveOrNegative(0)); // Zero
    System.out.println(positiveOrNegative(-1)); // Negative
    System.out.println(positiveOrNegative(-4)); // Negative
}
Michael Yaworski
  • 13,410
  • 19
  • 69
  • 97
  • 2
    -1 because Boolean's internal implementation of `compareTo` is: `return (x == y) ? 0 : (x ? 1 : -1);` which, according to requirements, is disallowed. – bbalchev Jun 29 '14 at 06:58
  • @bbalchev I knew that, but I was just hoping it would be an exception to the rule because we never saw it in the function. However, the code is still being used, so you're right. I've edited the answer now to eliminate all use of conditional statements. It's ugly, but at least it's correct. You can take away your downvote if you find this answer suitable now. – Michael Yaworski Jun 29 '14 at 08:16
  • 1
    The for loop is also disallowed. – Joe Jun 29 '14 at 18:15
  • @mikeyaworski probably because the for loop contains a conditional statement. – Joe Jun 29 '14 at 18:59
  • Theoretically correct, but other than that it is so bad, it starts to be funny. Try with `0x7FFFFFFF` (32 bit). Just in case it works without OOM crash or quasi-hang, scale that up to 64 bit and try again. – JensG Jun 29 '14 at 23:56
  • @JensG It's a silly question... I just put in my two cents. There's no reason to downvote just because you find it "funny". I just put out my way of doing it like everyone else did. – Michael Yaworski Jun 30 '14 at 00:14
  • What happens if `n` is a really large positive number? – Dawood ibn Kareem Jun 30 '14 at 00:48
  • @DavidWallace Use an if-statement or immibis's solution lol. Of course this will not work for large values. That's not what I was going for. I'm just putting something different out there. – Michael Yaworski Jun 30 '14 at 01:04
0

Another possible solution:

boolean isPositive(int n) {
  return n > ((n + 1) % n);
}

It does not work for 0 though.

---- EDIT -----

New algorithm:

String isPositive(int n) {
  String[] results = {"-", "", "+"};
  return results[1+(1+((n+1)%n)*((n-1)%n))/n];
}

It still does not work for 0.

fajarkoe
  • 1,543
  • 10
  • 12
0

Folks, it's not really hard, don't need to shift bits or do weird calls, just use the signum method in the Math class! ;p

http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#signum%28float%29
0

This is a very simple question than it sounds. This is possible without using conditional; just write this code

System.out.println(n>0);

Giorgio
  • 1,973
  • 4
  • 36
  • 51