Should conditional compilation be used to cope with difference in CGFloat on different architectures?

Solution 1:

Matt,

Building on your solution, and if you use it in several places, then a little extension might make it more palatable:

extension CGFloat {
    var ceil: CGFloat {
        #if arch(x86_64) || arch(arm64)
            return ceil(x)
        #else
            return ceilf(x)
        #endif
    }
}

The rest of the code will be cleaner:

var x = CGFloat(0.5)
x.ceil

Solution 2:

    var f : CGFloat = 0.5
    var result : CGFloat
    result = CGFloat(ceil(Double(f)))

Tell me what I'm missing, but that seems pretty simple to me.

Solution 3:

Note that with current version of Swift the solution below is already implemented in the standard library and all mathematical functions are properly overloaded for Double, Float and CGFloat.

Ceil is an arithmetic operation and in the same way as any other arithmetic operation, there should be an overloaded version for both Double and Float.

var f1: Float = 1.0
var f2: Float = 2.0

var d1: Double = 1.0
var d2: Double = 2.0

var f = f1 + f2
var d = d1 + d2

This works because + is overloaded and works for both types.

Unfortunately, by pulling the math functions from the C library which doesn't support function overloading, we are left with two functions instead of one - ceil and ceilf.

I think the best solution is to overload ceil for Float types:

func ceil(f: CFloat) -> CFloat {
    return ceilf(f)
}

Allowing us to do:

var f: Float = 0.5
var d: Double = 0.5

var f: Float = ceil(f)
var d: Double = ceil(d)

Once we have the same operations defined for both Float and Double, even CGFloat handling will be much simpler.

To answer the comment:

Depending on target processor architecture, CGFloat can be defined either as Float or a Double. That means we should use ceil or ceilf depending on target architecture.

var cgFloat: CGFloat = 1.5

//on 64bit it's a Double
var rounded: CGFloat = ceil(cgFloat)

//on 32bit it's a Float
var rounded: CGFloat = ceilf(cgFloat)

However, we would have to use the ugly #if.

Another option is to use clever casts

var cgFloat: CGFloat = 1.5
var rounded: CGFloat = CGFloat(ceil(Double(cgFloat))

(casting first to Double, then casting the result to CGFloat)

However, when we are working with numbers, we want math functions to be transparent.

var cgFloat1: CGFloat = 1.5
var cgFloat2: CGFloat = 2.5

// this works on both 32 and 64bit architectures!
var sum: CGFloat = cgFloat1 + cgFloat 2

If we overload ceil for Float as shown above, we are able to do

var cgFloat: CGFloat = 1.5

// this works on both 32 and 64bit architectures!
var rounded: CGFloat = ceil(cgFloat)