3
votes

I'm building 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 the vector I run into an issue.

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

The sqrt function of Swift only accepts Double as an argument but no generic argument.

Is there any way to use the sqrt function with generic parameters?

Here is a snippet of the code I use for the vector length and the 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
Even for vectors of integers, the length would usually be a floating point number (e.g. length([1, 1] = sqrt(2)), so you could cast to Double generally. – Btw, where is ArithmeticType defined? - Martin R
why don't you write your own sqrt function. this seems like a lot of extra bloated code to me just to get something like c11 functionality. then you can customize the integers to do either ceil, floor, etc. or use an arbitrary precision lib - μολὼν.λαβέ

3 Answers

2
votes

In Swift 3, just use the FloatingPoint protocol that is part of the standard library instead of your ArithmeticType protocol. Floatand Double conform to the FloatingPoint protocol. The FlotingPoint protocol has a squareRoot() method, so

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

should do the trick.

No need to import any libraries or do any run-time type checking! Invoking this method turns into an LLVM built-in, so there isn't even any function calling overhead. On an x86, sqareRoot() should just generate a single machine language instruction, leaving the result in a register for the return statement to copy.

1
votes

I see that you're using using a custom Arithmetic protocol to constraint the generic.

My approach would be to declare 2 required methods in that protocol: toDouble() and fromDouble(), and implement both in Float, Double and Int extensions. Note that fromDouble() should be a static method.

This way you can convert T to Double, hence be able to use sqrt(), and convert back from Double to T.

Last, there's a bug in your code: if left is an empty vector, the function will crash, because the code in the loop will never be executed, so result will keep its nil initial value. The forced unwrapping in the return statement will fail, causing the exception.

0
votes

There is no generic 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