Binary Search Implementation for Integer Array Purpose

Implement a binary search algorithm that takes a sorted int array and some int target value and returns the index of the
target value, if it exists.

Discussion

The way I thought about implementing a binary search algorithm was the following


Given an array of sorted int values, pick the first, last, and middle indices (round down if there is an even number of indices).
If the target value equals any of the values at the first/ last / middle index, return that index.
Else, check to see if the target value is less / greater than the middle index value.


If it's less than the middle index value, than we can assume that the target value, if it exists,
is between the first index and the middle index (since the array should be sorted). Thus, we can call our search
function on this sub-array, specifying a start index of the first index + 1 (since we already checked this)
and an end index of the middle index - 1 (again, since we already checked this).
If it's greater than the middle index value, we can assume that the target value is between the middle index and the last index.
Thus, we can call the search function on this sub-array, specifying a start index of the middle index + 1 and an
end index of the last index - 1.

We continue this process of analyzing the sub-arrays until our sub-array is empty, in which case a NoSuchElementException
is thrown.


Things that I'm unsure about


Does it make sense to have the public search method immediately call the private searchSubArray method?
The reason I did this is to obfuscate the idea of a leftIndex / rightIndex from the API user.
Does the NoSuchElementException make sense? Would returning -1 make more sense?
Any particular thoughts on my use of early returns?


Implementation

import java.util.NoSuchElementException;

public class BinarySearcher {
    /**
     * Executes a binary search for some target value given an input array of sorted values
     * @param values a sorted array
     * @param target a value to search for
     * @return the index of the target value, if it exists. If the target value does not exist, a NoSuchElementException is thrown.
     */
    public static int search(int[] values, int target) {
        return searchSubArray(values, 0, values.length - 1, target);
    }

    private static int searchSubArray(int[] values, int leftIndex, int rightIndex, int target) {
        if (values.length == 0 || leftIndex &lt; 0 || rightIndex &lt; 0) {
            throw new NoSuchElementException(String.format("Unable to find target: {} in values", target));
        }

        if (target == values[leftIndex]) {
            return leftIndex;
        }

        if (target == values[rightIndex]) {
            return rightIndex;
        }

        int middleIndex = Double.valueOf(Math.floor((rightIndex - leftIndex) / 2)).intValue();

        int middleValue = values[middleIndex];

        if (target &lt; middleValue) {
            return searchSubArray(values, leftIndex + 1, middleIndex - 1, target);
        }

        else if (target &gt; middleValue) {
            return searchSubArray(values, middleIndex + 1, rightIndex - 1, target);
        }

        else {
            return middleIndex;
        }
    }
}
Integer division does the right rounding. There is no need to bring in a double machinery.
Search and related algorithms are cleaner on the semi-open intervals (that is, the right boundary is one past the last element).
Testing first and last element looks like a premature optimization. In overwhelming number of cases it is just a waste of time.
Java does not detect and/or eliminate tail recursion. The algorithm can and should be made iterative.


Edit: tail recursion elimination.

Once you found that the recursion is really a tail one, the rest is fairly mechanical.


Rewrite the code to explicit tail form:

if (left &gt;= right) {
    doWhateverNotFoundActionYouWant();
}

middle = left + (right - left)/2;
midValue = value[middle];

if (target &lt; midValue) {
    right = middle;
} else if (target &gt; midValue) {
    left = middle + 1;
} else {
    return middle;
}
return recurse(value, left, right);

Remove the recursion, and replace if with while (don't forget to invert the condition):

while (left &lt; right) {
    middle = left + (right - left)/2;
    midValue = value[middle];

    if (target &lt; midValue) {
        right = middle;
    else if (target &gt; midValue) {
        left = middle + 1;
    } else {
        return middle;
    }
}