44

What is the exponent operator in Kotlin. I assumed it would be ** but it seems to throw up an error in my code.

when (pendingOperation) {
    "=" -> operand1 = value
    "÷" -> operand1 = if (value == 0.0) {
        Double.NaN // handle attempt to divide by zero
    } else {
        operand1!! / value
    }
    "x" -> operand1 = operand1!! * value
    "−" -> operand1 = operand1!! - value
    "+" -> operand1 = operand1!! + value
    "a^b" -> operand1 = operand1!! ** value
Pavneet_Singh
  • 36,884
  • 5
  • 53
  • 68
Keith Colclough
  • 455
  • 1
  • 5
  • 6

5 Answers5

53

Kotlin, like Java, does not have an exponent operator. Java has Math.pow, which you can use with Kotlin too, but Kotlin also has extension functions for Float and Double that you can use instead.

Should you need to use exponents with Ints or Longs, you just convert to double and back to int/long afterwards. Alternatively you can create your own methods.

It's pretty straightforward since it's an extension function; just call .pow on a Double or Float object:

"a^b" -> operand1 = operand1!!/*.toDouble()*/.pow(value)/*.toInt()*/
//Not sure what type operand1 is, so the comments are there if it's not a double or float, and the second assumes it's an int

Note: due to limitations of DEX, the rest of this answer DOES NOT work on Android. Use .pow() or Math.pow() instead. This still works on the JVM (and presumably Kotlin Native), but not Android.


You can, however, create some infix functions to kinda get one. The idea here is using the escape chars for Kotlin names to create a Python-style exponent operator. The escape chars are meant to escape Java functions with names colliding with Kotlin keywords, but they allow for a lot of fun. You can, for instance, have names with spaces, assuming you wrap it in backticks (`).

You can also write your own power function entirely from scratch, but note that Math.pow()'s implementation is written in C++, and will most likely be faster than one written in Kotlin or Java.

/**
 * Integer power using [Double.pow]
 */
infix fun Int.`**`(exponent: Int): Int = toDouble().pow(exponent).toInt()

/**
 * Long power using [Double.pow]
 * Note: it may be preferable to use a BigInteger instead of toDouble()
 * to prevent a loss of precision - use whichever makes sense
 * for the number you have at hand, and the precision you need.
 */
infix fun Long.`**`(exponent: Int): Long = toDouble().pow(exponent).toLong()
// infix fun Long.`**`(exponent: Int): Long = toBigInteger().pow(exponent).toLong()

/**
 * Double power using [Double.pow]
 */
infix fun Float.`**`(exponent: Int) : Float = this.pow(exponent)

/**
 * Float power using [Float.pow]
 */
infix fun Double.`**`(exponent: Int) : Double = this.pow(exponent)

Which allows you to call:

val x = 10
val exponent = 2
println(x `**` exponent)
assertEquals(x `**` exponent, 100)

Notice the backticks (``). As I mentioned earlier, in Kotlin, these are intended to escape java calls, when a function, variable, class, or something else contains a Kotlin keyword. For an instance, if I declare a boolean is() in Java and want to call it from Kotlin, I'd have to call it as ContainingClass.`is`.

However, they're not limited to use when calling Java functions or variables. You can use them as actual variable or function names. I.e. var `this` could be a variable name, but has to be called/dereferenced as `this`.

This is perfectly valid code:

var `this` = 32
`this` += 10;
print(`this`)

... but if you run it in a class, and use print(this) instead of using print(`this`), you print the result of the class' toString() method instead of 42.

Additionally, the backticks do not care what's escaped. Kotlin keyword or not, everything is escaped. You can have spaces, symbols, emojis (so you can finally have that val `` = 42; you've always wanted), keywords... That's what I've used in the function declaration. By declaring it with the escape characters, it makes a normally illegal name legal, presumably because it's interpreted as a raw string instead. I'm not entirely sure how it works under the hood, but it doesn't really matter when used. There's actually very few real uses for these aside interacting with Java-based libraries, but there's a lot of fun to be had too.

The second part of what makes the function work is the infix keyword. If you don't know what the infix keyword is, it enables calling functions without periods and parentheses. The reason it's used here is to make x `**` exponent an actual valid function call - without it, the code would be field.`**`(2). You can read more about infix functions in the documentation

You can also pick a different name to get rid of the backticks - I just used **, mainly because it's Python-like. It's also used in JavaScript and presumably some other languages as well. Other illegal names can also be picked by using backticks.

Zoe
  • 27,060
  • 21
  • 118
  • 148
  • using backticks (``) to define names for functions is only possible in tests! Also it is not supported in android platform (DEX files) – Mr.Q Apr 05 '20 at 15:10
  • 1
    @Mr.Q [No repro](https://try.kotlinlang.org/#/UserProjects/acqbd79kvs3hnf9j3vnlnnjk2l/m8s7biuu897srnqgao6qst3nvq). Android was never mentioned in the question, and I have no intention of writing answers with Android as my main concern when it isn't listed. Just like Android isn't Java, Kotlin Android isn't Kotlin. Use `.pow()` or `Math#pow()` instead – Zoe Apr 05 '20 at 15:15
  • 1
    @Mr.Q backticks are primarily meant for [escaping keywords](https://kotlinlang.org/docs/reference/java-interop.html#escaping-for-java-identifiers-that-are-keywords-in-kotlin) to facilitate Java-Kotlin interop. It's not limited to tests, at least with actual Kotlin. A post on [the forums](https://discuss.kotlinlang.org/t/android-issue-with-backticked-method-names/66/4) suggest it's an Android DEX thing, in which case, this can't be used. This still applies for Kotlin - Android was never an intended target with the workaround. – Zoe Apr 05 '20 at 15:42
  • The reason you get the appearance it only works with tests is because standard tests (not androidTest) run on the JVM, and not Dalvik. The real JVM (as in not Dalvik) still supports it. – Zoe Apr 05 '20 at 15:43
20

As other answers have mentioned there is no exponent operator in Kotlin/Java. However, it is not advisable to use the extension function available in Double and Float for performing exponentiation of Long. Here's why: Conversion of Double back to Long after exponentiation can lead to rounding of number due to precision limits of Double.

val EXP = 38

println(3.toDouble().pow(EXP).toLong())
// 1350851717672992000

println(3.toDouble().pow(EXP))
// 1.350851717672992E18 

But the actual answer was 1350851717672992089. Thus, I'd advise you to use BigIntegers to perform exponentiation. The same can also be used for fast modulo exponentiation. Here's our final pow(base, exp) function:

fun pow(n: Long, exp: Int): Long{
    return BigInteger.valueOf(n).pow(exp).toLong()
}
Satyam Kumar
  • 321
  • 2
  • 5
10

use extension method pow

inline fun Double.pow(x: Double): Double (source)

for more detail pow

Mostafa Anter
  • 3,445
  • 24
  • 26
-2

Here's another way to solve this problem:

   "a^b" -> operand1 = (operand1!! + 0.0).pow(value + 0.0).toInt()
Taslim Oseni
  • 6,086
  • 10
  • 44
  • 69
Jean_K
  • 51
  • 7
-3

If you need a cross platform version that does not depend on java.lang or Kotlin's Double.pow, this simple implementation will work:

fun pow(value: Long, exp: Int): Long {
    return when {
        exp > 1 -> value * pow(value,exp-1)
        exp == 1 -> value
        else -> 1
    }
}
Jilles van Gurp
  • 7,927
  • 4
  • 38
  • 46
  • 1
    This uses tail recursion which _is not_ optimized in Kotlin. This algorithm is very inefficient and will stack overflow for large values of `exp`: `>>> pow(1, 1000000)`: `java.lang.StackOverflowError` – Yona Appletree Jan 25 '21 at 23:36
  • Actually pow(1,whatever) would exit immediately without recursing. You'll overflow MAX_LONG, long before you stack overflow. But you are right that recursion can be problematic with large exponents, so could rewrite this to be a loop if that concerns you. – Jilles van Gurp Jan 26 '21 at 07:50