简体   繁体   中英

Swift generic number types and math

I'm trying to wrap my head around the ins and outs of Swift generics and make some common math functions. I'm trying to implement a mod function, but not quite sure the best way to get it working using generics.

Here's what my mod function looks like:

func mod<N: NumericType, I: IntegerType>(_ x: N, _ y: I) -> N {
    return x - y * floor(x/y)
}

But I'm getting this error:

error: binary operator '/' cannot be applied to operands of type 'N' and 'I'
    return x - y * floor(x/y)

And here's my NumericType declaration for decimal and integer type numbers:

protocol NumericType: Comparable {
    static func +(lhs: Self, rhs: Self) -> Self
    static func -(lhs: Self, rhs: Self) -> Self
    static func *(lhs: Self, rhs: Self) -> Self
    static func /(lhs: Self, rhs: Self) -> Self
    static func %(lhs: Self, rhs: Self) -> Self
}

protocol DecimalType: NumericType {
    init(_ v: Double)
}

protocol IntegerType: NumericType {
    init(_ v: Int)
}

extension CGFloat : DecimalType { }
extension Double  : DecimalType { }
extension Float   : DecimalType { }

extension Int     : IntegerType { }
extension Int8    : IntegerType { }
extension Int16   : IntegerType { }
extension Int32   : IntegerType { }
extension Int64   : IntegerType { }
extension UInt    : IntegerType { }
extension UInt8   : IntegerType { }
extension UInt16  : IntegerType { }
extension UInt32  : IntegerType { }
extension UInt64  : IntegerType { }

As of Swift 3, all floating point types conform to FloatingPoint , and all integer types conform to Integer . Both protocols define the basic arithmetic operations like +,-,*,/. Also the floor() function is defined for FloatingPoint arguments.

Therefore in your case I would define two implementations, one for integers and one for floating point values:

func mod<N: Integer>(_ x: N, _ y: N) -> N {
    return x - y * (x/y) // or just: return x % y
}

func mod<N: FloatingPoint>(_ x: N, _ y: N) -> N {
    return x - y * floor(x/y)
}

FloatingPoint has also a truncatingRemainder method, a.truncatingRemainder(b) is the "floating point equivalent" to a % b for integers. It gives the gives the same result as your mod function if both operands have the same sign.

static func /(lhs: Self, rhs: Self) -> Self

This means both lhs and rhs has to be of the same type .

In x / y , x is of the type N , and y is of type I . So x and y have different types and therefore x / y does not work.

You need to cast y from I to N first:

let numericY = N(integer: y)
return x - numericY * floor(x / numericY)

which means your NumericType need to be able to initialize from an IntegerType, besides having + , - , * , / , % .

protocol NumericType: Comparable {
    init<I: IntegerType>(integer: I)
    ...
}

(Also, floor<N: NumericType>(n: N) -> N should exist to make the whole expression compile.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM