Conversion between CGFloat and NSNumber without unnecessary promotion to Double

Update: One can cast a CGFloat value to NSNumber and back:

let c1 = CGFloat(12.3)
let num = c1 as NSNumber
let c2 = num as CGFloat

This preserves the precision of CGFloat and works with Swift 2 and Swift 3.


(Previous answer – far too complicated): There are two solutions that I found. The first uses the toll-free bridging between NSNumber and CFNumber (as in What is most common and correct practice to get a CGFloat from an NSNumber? for Objective-C). It uses the fact that CFNumber has a dedicated conversion mode for CGFloat values:

extension NSNumber {

    // CGFloat -> NSNumber
    class func numberWithCGFloat(var value: CGFloat) -> NSNumber {
        return CFNumberCreate(nil , .CGFloatType, &value)
    }

    // NSNumber -> CGFloat
    var cgFloatValue : CGFloat {
        var value : CGFloat = 0
        CFNumberGetValue(self, .CGFloatType, &value)
        return value
    }
}

That is simple and nice. The only drawback: I could not figure out how to make the constructor an init method instead of a class method.

The second possible solution is a bit longer:

extension NSNumber {

    // CGFloat -> NSNumber
    private convenience init(doubleOrFloat d : Double) {
        self.init(double : d)
    }
    private convenience init(doubleOrFloat f : Float) {
        self.init(float : f)
    }
    convenience init(cgFloat : CGFloat) {
        self.init(doubleOrFloat: cgFloat.native)
    }

    // NSNumber -> CGFloat
    private func doubleOrFloatValue() -> Double {
        return self.doubleValue
    }
    private func doubleOrFloatValue() -> Float {
        return self.floatValue
    }
    var cgFloatValue : CGFloat {
        return CGFloat(floatLiteral: doubleOrFloatValue())
    }
}

There are two private "helper" init methods with the same external parameter name doubleOrFloat but different parameter types. From the actual type of cgFloat.native the compiler determines which one to call in

    convenience init(cgFloat : CGFloat) {
        self.init(doubleOrFloat: cgFloat.native)
    }

Same idea in the accessor method. From the type of self.native the compiler determines which of the two doubleOrFloatValue() methods to call in

    var cgFloatValue : CGFloat {
        return CGFloat(floatLiteral: doubleOrFloatValue())
    }