4

I know that in Java (and probably other languages), Math.pow is defined on doubles and returns a double. I'm wondering why on earth the folks who wrote Java didn't also write an int-returning pow(int, int) method, which seems to this mathematician-turned-novice-programmer like a forehead-slapping (though obviously easily fixable) omission. I can't help but think that there's some behind-the-scenes reason based on the intricacies of CS that I just don't know, because otherwise... huh?

On a similar topic, ceil and floor by definition return integers, so how come they don't return ints?

Thanks to all for helping me understand this. It's totally minor, but has been bugging me for years.

djechlin
  • 59,258
  • 35
  • 162
  • 290
Katie Byers
  • 822
  • 8
  • 16
  • 1
    I think it comes down to the fact that CPU have special instructions to do Math.pow for floating point numbers (but not for integers). – Thilo Jul 24 '14 at 22:59
  • For `Math.pow`, ints (and longs) can be converted to double, so they are taken care of automatically. – markspace Jul 24 '14 at 23:01
  • 1
    I would think they were just copying C's math.h. – David Conrad Jul 24 '14 at 23:03
  • @markspace: except, I do seem to have to cast any int to a double before my compiler will accept it... – Katie Byers Jul 24 '14 at 23:03
  • 2
    Comes to mind that exponents of ints can get very big. BigInteger does offer exponent. – djechlin Jul 24 '14 at 23:04
  • @Katie: yes, that was my point. It happens automatically, no need to do anything at all. Hence "automatically" in my comment above. – markspace Jul 25 '14 at 00:21
  • The real reason is that the market demand for floating point exponentiation is vastly greater than the demand for integer exponentiation in the upper ranges of the `long` type. And `BigInteger.pow` exists for larger integer needs. – President James K. Polk Apr 18 '23 at 20:43

5 Answers5

4

java.lang.Math is just a port of what the C math library does.

For C, I think it comes down to the fact that CPU have special instructions to do Math.pow for floating point numbers (but not for integers).

Of course, the language could still add an int implementation. BigInteger has one, in fact. It makes sense there, too, because pow tends to result in rather big numbers.

ceil and floor by definition return integers, so how come they don't return ints

Floating point numbers can represent integers outside of the range of int. So if you take a double argument that is too big to fit into an int, there is no good way for floor to deal with it.

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • Not all of it is JNI-glued btw. See [here](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/StrictMath.java) for the default implementation. Take a look at `#round()` for example. – AlexR Jul 24 '14 at 23:07
2

From a mathematical perspective, you're going to overflow your integer if it's larger than 231-1, and overflow your long if it's larger than 264-1. It doesn't take much to overflow it, either.

Doubles are nice in that they can represent numbers from ~10-308 to ~10308 with 53 bits of precision. There may be some fringe conversion issues (such as the next full integer in a double may not exactly be representable), but by and large you're going to get a much larger range of numbers than you would if you strictly dealt with integers or longs.

On a similar topic, ceil and floor by definition return integers, so how come they don't return ints?

For the same reason outlined above - overflow. If I have an integral value that's larger than what I can represent in a long, I'd have to use something that could represent it. A similar thing occurs when I have an integral value that's smaller than what I can represent in a long.

Makoto
  • 104,088
  • 27
  • 192
  • 230
1

Optimal implementation of integer pow() and floating-point pow() are very different. And C's math library was probably developed around the time when floating-point coprocessors were a consideration. Optimal implementation of floating point operation is to shift the numbers closer to 1 (to force quicker conversion of the power series) and then shift the result back. For integer power, a more accurate result can be had in O(log(p)) time by doing something like this:

// p is a positive integer power set somewhere above, n is the number to raise to power p
int result = 1;
while( p != 0){
    if (p & 1){ 
        result *= n;
    }
    n = n*n; 
    p = p >> 1;
}
Dmitry Rubanovich
  • 2,471
  • 19
  • 27
0

Because all ints can be upcast to a double without loss and the pow function on a double is no less efficient that that on an int.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • no less efficient computation-wise, okay, but readability/efficiency-of-typing-wise, it'd save a lot not to have *three* casts just to take 5^7 or whatever... – Katie Byers Jul 24 '14 at 23:16
  • @Katie - Yes you have to cast the result if you must have an `int` but you don't need to cast the `int` parameters - they up-cast automagically. What if you get a `long` result or even bigger though? `(int)Math.pow(10,300)` – OldCurmudgeon Jul 24 '14 at 23:23
  • even if it's equally efficient, it doesn't mean it's equally accurate. – Dmitry Rubanovich Nov 30 '17 at 10:26
-1

The reason lies behind the implementation of Math.pow() (JNI of default implementation). The CPU has an exponentiation module which works with doubles as input and output. Why should Java convert that for you when you have much better control over this yourself?

For floor and ceil the reasons are the same, but note that:

(int) Math.floor(d) == (int) d;   // d > 0
(int) Math.ceil(d) == -(int)(-d); // d < 0

For most cases (no warranty around or beyond Integer.MAX_VALUE or Integer.MIN_VALUE).

Java leaves you with

(int) Math.pow(a,b)

because the result of Math.pow may even be NaN or Infinity depending on input.

AlexR
  • 2,412
  • 16
  • 26
  • Why should Java do anything for you when you have control over it yourself? – djechlin Jul 24 '14 at 23:03
  • @djechlin Well, in this particular case it wouldn't save you a single line of code... – AlexR Jul 24 '14 at 23:04
  • 2
    In the second example you gave... does int-casting always round down? I thought it truncated (so that (int) (-4.5) would be -4). No? – Katie Byers Jul 24 '14 at 23:07
  • @Katie Yeah neither of those lines are quite right. Top one is valid for positive numbers and the bottom one for negatives. – Tavian Barnes Jul 24 '14 at 23:08
  • @TavianBarnes ah, okay, that makes more sense, thanks – Katie Byers Jul 24 '14 at 23:09
  • "Why should Java convert that for you when you have much better control over this yourself?" Well, yes, I can make it work by casting everything to doubles and then back to ints, but `a = Math.pow(c,d)` seems to me a whole lot more readable than `a = (int) Math.pow((double)c,(double)d)`, and it's also a lot less to type -- so that's why I'd expect java to do it for me. – Katie Byers Jul 24 '14 at 23:15
  • @Katie confirmed the caveat. Note that `(int) Math.pow(c,d)` will be desugared into `(int) Math.pow((double) c, (double) d)` anyways, so no need to explicitly cast the arguments. – AlexR Jul 24 '14 at 23:16
  • @AlexR int-casting does not round down, it truncates. `(int) -4.5 == -4`. – Tavian Barnes Jul 24 '14 at 23:20
  • Are you guaranteed that.e.g Math.pow(3,3) >= 27? Could you get Math.pow(3,3) = 26.999999996? That would be a strong case for an int- version. – djechlin Jul 24 '14 at 23:29
  • djechlin: You are guaranteed that Math.pow(3,3) == 27. Just read the API documentation. – jarnbjo Jul 25 '14 at 06:32
  • @djechlin Actually, a long time ago some intel processors had a [similar mistake](http://en.wikipedia.org/wiki/Pentium_FDIV_bug) to what you fear, but this would be considered a hardware fault. If you want to get those on current processors, you can overclock them too much and have too low voltage + too low cooling, increasing the likelihood of such an error. ^^ – AlexR Jul 25 '14 at 13:02