Should Conditional Compilation Be Used to Cope With Difference in Cgfloat on Different Architectures

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

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

How to determine if CGFloat is Float or Double

Documentation on Swift Floating-Point Numbers:

Floating-point types can represent a much wider range of values than
integer types, and can store numbers that are much larger or smaller
than can be stored in an Int. Swift provides two signed floating-point
number types:

  • Double represents a 64-bit floating-point number.
  • Float represents a 32-bit floating-point number.

You can test using the sizeof function:

if sizeof(CGFloat) == sizeof(Double) {
// CGFloat is a Double
} else {
// CGFloat is a Float
}

Probably the easiest way to deal with this is to use conditional compilation to define a wrapper which will call the proper version:

import Accelerate

func getrf_(__m: UnsafeMutablePointer<__CLPK_integer>,
__n: UnsafeMutablePointer<__CLPK_integer>,
__a: UnsafeMutablePointer<CGFloat>,
__lda: UnsafeMutablePointer<__CLPK_integer>,
__ipiv: UnsafeMutablePointer<__CLPK_integer>,
__info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 {

#if __LP64__ // CGFloat is Double on 64 bit archetecture
return dgetrf_(__m, __n, UnsafeMutablePointer<__CLPK_doublereal>(__a), __lda, __ipiv, __info)
#else
return sgetrf_(__m, __n, UnsafeMutablePointer<__CLPK_real>(__a), __lda, __ipiv, __info)
#endif
}

Convert CGFloat to CFloat in Swift

For some reason I needed to make an explicit typecast to CDouble to make it work.

let roundedWidth = round(CDouble(CGRectGetWidth(self.bounds)))

I find this pretty strange since a CGFloat is a CDouble by definition. Seems like the compilator got a bit confused here for some reason.

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())
}

Cannot find overload for '/', '*', Failure when running an app on anything but iPhone 5s simulator

Your problem is a difference between 32- and 64-bit architecture. Note that the target architecture you're compiling your target for is determined by the selected device—if you've got the iPhone 4S simulator selected as your target in Xcode, for example, you'll be building for 32 bit; if you've got the iPhone 5S simulator selected, you'll be building for 64-bit.

You haven't included enough code to help us figure out what exactly is going on (we'd need to know the types of the variable you're assigning to) but here's my theory. In your first error, sprite.speed is probably a CGFloat. CGFloat is 32-bit ("float") on 32-bit targets, 64-bit ("double") on 64-bit targets. So this, for example:

var x:CGFloat = Double(arc4random()) / 0x100000000

...will compile fine on a 64-bit target, because you're putting a double into a double. But when compiling for a 32-bit target, it'll give you the error that you're getting, because you're losing precision by trying to stuff a double into a float.

This will work on both:

var x:CGFloat = CGFloat(arc4random()) / 0x100000000

Your other errors are caused by the same issue (though again, I can't reproduce them accurately without knowing what type you've declared width and height as.) For example, this will fail to compile for a 32-bit architecture:

    let lengthDiceroll = Double(arc4random()) / 0x100000000
let width:CGFloat = 5
var y:CGPoint = CGPointMake(width * lengthDiceroll, 0)

...because lengthDiceroll is a Double, so width * lengthDiceroll is a Double. CGPointMake takes CGFloat arguments, so you're trying to stuff a Double (64-bit) into a float (32-bit.)

This will compile on both architectures:

    let lengthDiceroll = Double(arc4random()) / 0x100000000
let width:CGFloat = 5
var y:CGPoint = CGPointMake(width * CGFloat(lengthDiceroll), 0)

...or possibly better, declare lengthDiceroll as CGFloat in the first place. It won't be as accurate on 32-bit architectures, but that's sort of the point of CGFloat:

    let lengthDiceroll = CGFloat(arc4random()) / 0x100000000
let width:CGFloat = 5
var y:CGPoint = CGPointMake(width * lengthDiceroll, 0)

Round up a CGFloat in Swift

Update: Apple have now defined some CGFloat-specific versions of common functions like ceil:

func ceil(x: CGFloat) -> CGFloat

...specifically to cope with the 32/64-bit difference. If you simply use ceil with a CGFloat argument it should now work on all architectures.

My original answer:

This is pretty horrible, I think, but can anyone think of a better way? #if doesn't seem to work for CGFLOAT_IS_DOUBLE; I think you're limited to build configurations, from what I can see in the documentation for conditional compilation.

var x = CGFloat(0.5)

#if arch(x86_64) || arch(arm64)
var test = ceil(x)
#else
var test = ceilf(x)
#endif

iphone and iPad difference in Designing

I am assuming that you are opting for a universal executable, so conditional compilation is not an option for you.

When you make a universal executable, you should check the features that you are relying upon before making calls dependent on the device type. In this particular case you are relying upon the screen having a particular size. Instead of hard-coding the "magic numbers" (42, 15, 440, and 60) you should calculate them from the current size of the available screen:

CGFloat w = [UIScreen mainScreen].bounds.size.width;
CGFloat h = [UIScreen mainScreen].bounds.size.height;
// Do something like this if you can
UITextView *textview = [[UITextView alloc]
initWithFrame:CGRectMake(w*0.025, h*0.25, w*0.5, h*0.125)
];

There is a chance that calculating actual sizes from the screen size is not possible, because you do not want your view to scale proportionally to the screen. In cases like that you can check the values of h and w, detect the device size, and use the corresponding set of pre-defined sizes to init your view.



Related Topics



Leave a reply



Submit