3

I created this infix operator ^^ as a substitute to using the pow function:

infix operator ^^ { associativity left precedence 155 }
func ^^ <T: IntegerLiteralConvertible>(left: T, right: T) -> T {
    return pow(left as Double, right as Double)
}

I used the IntegerLiteralConvertible protocol as a type constraint for the generics left and right, because from my understanding this diagramm shows, that it basically includes all number types.

In order to use the pow function I have to downcast left and right to Double though, which I did using the as operator. It's not the safest approach, but that's besides the point.

When implementing the function this way swift tells me:

<stdin>:4:12: error: cannot invoke 'pow' with an argument list of type '(Double, Double)'
return pow(left as Double, right as Double)
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now as far as I know pow takes two Doubles as parameters, so why is it complaining about this?

Marcus Rossel
  • 3,196
  • 1
  • 26
  • 41
  • Hello from 2019! . Now, I see this declaration in Foundation for pow: `public func pow(_ x: Decimal, _ y: Int) -> Decimal` – gprasant Feb 01 '19 at 16:20

2 Answers2

3

Thanks @Martin R. This comes from the only question I have posted so far at S.O.

import Cocoa

Protocols

protocol Fraction { init(_ value:Double) ; var asDouble  : Double { get } }
protocol Text     { init(_ value:String) ; var asString  : String { get } }

Extensions

extension String  : Text     { var asString : String { return self } }

extension Double  : Fraction { var asDouble : Double { return self         } }
extension Float   : Fraction { var asDouble : Double { return Double(self) } }
extension CGFloat : Fraction { var asDouble : Double { return Double(self) } }

infix operator ^^

infix operator ^^ { associativity left precedence 170 }
func ^^<T:IntegerType, U:IntegerType> (var base:T, var power:U) -> T {
    if power < 0 { return 0 }
    var result: T = 1
    if power > 0 {
        if power % 2 == 1 { result *= base }
        power /= 2
    }
    while power > 0 {
        base *= base
        if power % 2 == 1 { result *= base }
        power /= 2
    }
    return result
}
func ^^<T:Fraction, U:Fraction> (base: T, power: U) -> T {
    return T(pow(base.asDouble, power.asDouble))
}
func ^^<T:Text, U:IntegerType> (base:T, var power:U) -> T {
    if power  <= 0 { return "" as T }
    if power  == 1 { return base as T }
    return power > 1 ? {var result = ""; for x in 1...power  { result+=base.asString };return result as T}() : "" as T
}
func ^^<T:Text, U:Text> (base:T, var power:U) -> T {
    return "" as T
}

testing

println(1 ^^ -1)               // "0"      Int
println(1 ^^  0)               // "1"      Int
println(1 ^^  1)               // "1"      Int
println(1 ^^  2)               // "1"      Int

println(2 ^^ -1)               // "0"           Int
println(2 ^^  0)               // "1"           Int
println(2 ^^  1)               // "2"           Int
println(2 ^^  2)               // "4"           Int
println(2 ^^  8)               // "256"         Int
println(2 ^^  16)              // "65536"       Int
println(2 ^^  32)              // "4294967296"  Int

println(2 ^^  62)               // "4611686018427387904"

println(UInt(2) ^^ 8)          // "256"      UInt
println(UInt64(2) ^^ 8)        // "256"      UInt64
Community
  • 1
  • 1
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • http://stackoverflow.com/questions/26794282/how-do-i-make-my-operator-work-with-double-and-int – Leo Dabus Feb 24 '15 at 19:15
  • You are not searching for a compact/elegant solution. You are looking for an easy solution and avoiding to implement the protocols needed in this case. If you look at my second solution you can test if base is of type and avoid using protocols. You have enough info there to choose a path to make it type safe or just keep your operator restricted to certain types using your more elegant way of not doing what actually needs to be done. need. – Leo Dabus Feb 24 '15 at 20:14
  • No. In the past I've just been exposed to parts of swift which I didn't previously know of, which simplified the whole problem. That's kind of what I'm waiting for here. – Marcus Rossel Feb 24 '15 at 20:30
  • Sorry to tell you that you will wait in vain. Take a look when was my question posted. – Leo Dabus Feb 24 '15 at 20:41
3

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.

rintaro
  • 51,423
  • 14
  • 131
  • 139