Common square root in Swift

I am creating a generic vector class in Swift with three types: Float, Double and Int. This works so far, but when I try to calculate the length of a vector, I run into a problem.

The formula for vector length is the square root of (xΒ² + yΒ²). But since I am using a generic class for my vectors, the x and y values ​​are named T.

The sqrt Swift function only accepts Double as an argument, but has no common argument.

Can the sqrt function be used with shared parameters?

Here is the piece of code I am using for the vector length and dot product:

protocol ArithmeticType {
    func + (left: Self, right: Self) -> Self
    func - (left: Self, right: Self) -> Self
    func * (left: Self, right: Self) -> Self
    func / (left: Self, right: Self) -> Self
    prefix func - (left: Self) -> Self

    func toDouble() -> Double
}

extension Double: ArithmeticType {
    func toDouble() -> Double {
        return Double(self)
    }
}

extension Float: ArithmeticType {
    func toDouble() -> Double {
        return Double(self)
    }
}

extension Int: ArithmeticType {
    func toDouble() -> Double {
        return Double(self)
    }
}

class Vector<T where T: ArithmeticType, T: Comparable> {
    var length: T { return sqrt((self β‹… self).toDouble()) }
}

infix operator β‹… { associativity left }
func β‹…<T: ArithmeticType> (left: Vector<T>, right: Vector<T>) -> T {
    var result: T? = nil

    for (index, value) in enumerate(left.values) {
        let additive = value * right.values[index]

        if result == nil {
            result = additive
        } else if let oldResult = result {
            result = oldResult + additive
        }
    }

    if let unwrappedResult = result {
        return unwrappedResult
    }
}

      

+3


source to share


3 answers


I see you are using a custom protocol Arithmetic

to limit the general.

My approach would be to declare the 2 required method in this protocol: toDouble()

and fromDouble()

and implemented as extensions Float

, Double

as well Int

. Note that it fromDouble()

must be a static method.



So you can convert T

to Double

, hence you can use sqrt()

and convert back from Double

to T

.

Finally, there is an error in your code: if left

is an empty vector, the function will fail because the code in the loop will never be executed, so it will result

keep the initial value nil

. Forcing unwrapping in a statement return

will fail, throwing an exception.

+1


source


In Swift 3, just use a protocol FloatingPoint

that is part of the standard library instead of your protocol ArithmeticType

. Float

and Double

comply with the protocol FloatingPoint

. The protocol FlotingPoint

has a method squareRoot()

, so

class Vector<T where T: FloatingPoint> {
    var length: T { return (self β‹… self).squareRoot() }
}

      



must do the trick.

No need to import any libraries or runtime type checking! Calling this method turns into inline LLVM, so there are no function overhead calls. On x86, sqareRoot () should just generate one machine language instruction, leaving the result in register for a return statement to copy.

+2


source


There is no common sqrt in Swift. But you can make your own generic.

import Foundation // for sqrt sqrtf
public func sqrt<T:FloatingPointType>(v:T) -> T {
    if let vv = v as? Double {
        return sqrt(vv) as! T
    }
    if let vv = v as? Float {
        return sqrtf(vv) as! T
    }
    preconditionFailure()
}
print(sqrt(Float(9)))  // == 3
print(sqrt(Double(9))) // == 3

      

0


source







All Articles