Design Notes: Why Math.floor() does not return ‘int’

This is a commonly asked question. If Math.floor() and Math.ceil() will always return a whole number, why does it return double and not another integer type that represents only whole numbers like int or long? In Java, C#, and JS++, you’ll usually see a method signature as follows:

public static double floor(double x);

In actual usage, we may see scenarios such as:

int x = (int)Math.floor(10.5);

Hardly pretty. Why was it designed this way? Why couldn’t we just overload Math.floor() to return a different type based on whether we received an int, long, double, etc. argument?

To understand this, we must first understand that Math.floor() can take negative numbers as an argument. For example, Math.floor(-2.5) will return -3. Therefore, if we round down beyond the lowest 32-bit signed integer value (-2,147,483,648), we will no longer be within the range of an int (likewise for long). On the other end of the spectrum, we have Math.ceil(). If we round up beyond the highest 32-bit signed integer value (2,147,483,647), we will no longer be within the range of an int (and, again, likewise for long). However, a double (IEEE 754 double floating-point type) will fit both an int and long.

… But you might still be wondering whether or not we can still achieve this correctly through overloading (combined with wrapping or runtime exceptions for overflows)? Let’s assume we did. Math.floor() for an int when we receive an int, long when receive a long, etc. Look at this signature for a second; what’s wrong with it?

public static int floor(int x);

As you can see, this would defeat the purpose of Math.floor and Math.ceil. Why call Math.floor() on an int to receive the same number back?

You’re not going to have a decimal number to floor() or ceil() with int, long, etc. If we just make all the overloads accept a double argument, can’t we just return a different type? Sadly, no; it would be ambiguous, and it’s impossible to know whether we should return an int, long, etc. if the argument types were the same. Observe:

1
2
3
4
5
public static int floor(double x) { ... }
public static long floor(double x) { ... }
 
double x = 1;
floor(x); // do we return an int or long here?

Therefore, despite its inelegance, Math.floor() and Math.ceil() must return a double.