How to Make My Exponentiation Operator Work With All Numeric Types in Swift

How do I make my exponentiation operator work with all numeric types in Swift?

Here is a more type-safe implementation, using operator overloading. For integer types,
you can define ^^ as

infix operator ^^ { associativity left precedence 170 }

func ^^<T : IntegerType, U : IntegerType> (base: T, var power: U) -> T {

if power < 0 { return 0 }

var result : T = 1
var square : T = base

if power > 0 {
if power % 2 == 1 { result *= square }
power /= 2
}
while power > 0 {
square *= square
if power % 2 == 1 { result *= square }
power /= 2
}

return result
}

(I have chosen a more efficient variant called "exponentiation by repeated squaring and multiplication".)

For floating point types, define a protocol the covers all types that can be
converted from and to Double:

protocol DoubleConvertible {
init(_ value: Double)
var doubleValue : Double { get }
}

and make Float, Double and CGFloat conform to that protocol:

extension Double : DoubleConvertible {
var doubleValue : Double { return self }
}

extension Float : DoubleConvertible {
var doubleValue : Double { return Double(self) }
}

extension CGFloat : DoubleConvertible {
var doubleValue : Double { return Double(self) }
}

Now the floating point exponentiation can simply be defined as

func ^^<T : DoubleConvertible, U:DoubleConvertible> (base: T, power: U) -> T {

return T(pow(base.doubleValue, power.doubleValue))
}

Examples:

let x1 = 2^^3               // Int
let x2 = UInt64(2)^^3 // UInt64
let x3 = 2.0 ^^ 3 // Double
let x4 = Float(2.0) ^^ 3 // Float
let x5 = "a" ^^ "b" // Compiler error

Update for Swift 4:

IntegerType has become BinaryInteger (SE-0104 Protocol-oriented integers) and
the syntax for declaring operators has changed
(SE-0077 Improved operator declarations.)

Here is an implementation for all integer and floating point
bases with Int exponents:

precedencegroup ExponentiationPrecedence { associativity: right higherThan: MultiplicationPrecedence }
infix operator ^^: ExponentiationPrecedence

func ^^<T : BinaryInteger>(base: T, power: Int) -> T {
if power < 0 { return 0 }
var power = power
var result: T = 1
var square = base

if power > 0 {
if power % 2 == 1 { result *= square }
power /= 2
}
while power > 0 {
square *= square
if power % 2 == 1 { result *= square }
power /= 2
}
return result
}

func ^^(base: Float, power: Int) -> Float {
return pow(base, Float(power))
}

func ^^(base: Double, power: Int) -> Double {
return pow(base, Double(power))
}

func ^^(base: CGFloat, power: Int) -> CGFloat {
return pow(base, CGFloat(power))
}

Examples:

let x1 = 2^^3            // Int
let x2 = UInt64(2)^^3 // UInt64
let x3 = 2.0 ^^ -3 // Double
let x4 = Float(2.0) ^^ 3 // Float
// let x6 = "a" ^^ 5 // Compiler error

See SE-0104 Protocol-oriented integers
about the new protocol hierarchy introduced in Swift 4.

Exponentiation operator in Swift

There isn't an operator but you can use the pow function like this:

return pow(num, power)

If you want to, you could also make an operator call the pow function like this:

infix operator ** { associativity left precedence 170 }

func ** (num: Double, power: Double) -> Double{
return pow(num, power)
}

2.0**2.0 //4.0

Swift How combine to Numeric Type

You don't have to re-invent the wheel, there is already protocol which you need: Numeric.

Its description is very self-descriptive:

A type with values that support multiplication

And the types like Int, Double, ... already conform to it.


Anyway, if you wonder why your code doesn't work, it's because protocol provides interface and tells what implementations should contain and what is possible to do with them.

So, if you need that your Numbers can be multiplied, you need to describe it in protocol's declaration.

protocol Number {
static func * (lhs: Self, rhs: Self) -> Self
}

... now you described that every Number can be multiplied by other Number. /p>

How to get the Power of some Integer in Swift language?

If you like, you could declare an infix operator to do it.

// Put this at file level anywhere in your project
infix operator ^^ { associativity left precedence 160 }
func ^^ (radix: Int, power: Int) -> Int {
return Int(pow(Double(radix), Double(power)))
}

// ...
// Then you can do this...
let i = 2 ^^ 3
// ... or
println("2³ = \(2 ^^ 3)") // Prints 2³ = 8

I used two carets so you can still use the XOR operator.

Update for Swift 3

In Swift 3 the "magic number" precedence is replaced by precedencegroups:

precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence }
infix operator ^^ : PowerPrecedence
func ^^ (radix: Int, power: Int) -> Int {
return Int(pow(Double(radix), Double(power)))
}

// ...
// Then you can do this...
let i2 = 2 ^^ 3
// ... or
print("2³ = \(2 ^^ 3)") // Prints 2³ = 8

How can I raise all the elements in an int array by a power of 3 in Swift?

You can use map() and pow() together:

import Foundation

let nums = [1, 2, 3, 4, 5, 6]
let cubes = nums.map { Int(pow(Double($0), 3)) }
let raisedBySix = nums.map { Int(pow(Double($0), 6)) }
print(cubes) // [1, 8, 27, 64, 125, 216]
print(raisedBySix) // [1, 64, 729, 4096, 15625, 46656]

Swift's pow() function won't accept Doubles as arguments

why is it complaining about this?

Because pow returns Double. And Double is not identical to T. The error message is misleading, but it means "Cannot find pow that accepts (Double, Double) and returns T type"


I think you are misunderstanding "cast" in Swift. In Swift as does not convert numeric types.

let intVal:Int = 12
let doubleVal:Double = intVal as Double
// ^ [!] error: 'Int' is not convertible to 'Double'

And If the type of operand is not predictable at compile time, invalid casting causes runtime error:

func foo<T: IntegerLiteralConvertible>(x: T)  {
x as Double // <-- Execution was interrupted
}
foo(12 as Int)

Instead, we must explicitly "convert" them. see the document: Numeric Type Conversion

let intVal:Int = 12
let doubleVal:Double = Double(intVal)

This works only because Double has init(_ v: Int) initializer. The following code does not compile:

func foo<T: IntegerLiteralConvertible>(x: T)  {
Double(x)
// ^~~~~~~~~ [!] error: cannot invoke 'init' with an argument of type 'T'
}

Because Double does not have init<T:IntegerLiteralConvertible>(_ val:T) initializer.

So, if you want to use pow(), you must convert T to Double for arguments, and convert Double to T for returning value. And there is no simple solution for that.



Related Topics



Leave a reply



Submit