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

不羁的心 提交于 2019-12-07 13:37:22

问题


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?


回答1:


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.




回答2:


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


来源:https://stackoverflow.com/questions/28702782/swifts-pow-function-wont-accept-doubles-as-arguments

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!