3

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
    }
}
  • 1
    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 Nov 02 '14 at 13:02
  • 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 – μολὼν.λαβέ Nov 16 '16 at 21:12

3 Answers3

2

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.

Paul Buis
  • 805
  • 7
  • 7
1

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.

Antonio
  • 71,651
  • 11
  • 148
  • 165
  • I have now implemented the toDouble() method in the ArithmeticType protocol and did `sqrt((self ⋅ self).toDouble())` in the length variable. But now Swift throws this error: `Cannot invoke 'sqrt' with an argument of type 'Double'`, while when I type `sqrt` Xcode autocompletes it and shows that sqrt(Double) is valid. The code inside my ArithmeticType protocol is now: protocol ArithmeticType { func toDouble() -> Double } extension Int: ArithmeticType { func toDouble() -> Double { return self } } The Float and Double implementations are equal –  Nov 02 '14 at 13:58
  • I think `func toDouble() -> Double { return self }` should be `func toDouble() -> Double { return Double(self) }` - you have to convert, as there's no implicit cast from `Int` to `Double` – Antonio Nov 02 '14 at 14:10
  • I now applied that change, but the sqrt function still expects a Double argument, even though I'm supplying it with a Double argument. It seems to be converting it correctly, because before the .toDouble() it gave me an argument of type T. –  Nov 02 '14 at 14:15
  • Can you post that code? Add an update in your question pls, so the code is more readable – Antonio Nov 02 '14 at 14:19
  • 2
    The type of the length property should now be Double instead of T. – Martin R Nov 02 '14 at 14:29
  • Side note: you should avoid changing the existing code in your question - that makes the question itself confusing to readers. It's always better to add a proper "Update" below your original question – Antonio Nov 02 '14 at 14:32
  • And yes, @MartinR is right, `var length: T` should be `var length: Double`, unless you want to convert it back to `T`, but in that case you also have to implement the `fromDouble()` static method – Antonio Nov 02 '14 at 14:34
  • Thanks guys, this solved it! And I will in the future not replace the code. –  Nov 02 '14 at 14:51
0

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