Conversion Between Cgfloat and Nsnumber Without Unnecessary Promotion to Double

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

NSNumber from CGFloat

I believe @ NSNumber literal is good enough

@(myCGFloat)

Read more here: http://clang.llvm.org/docs/ObjectiveCLiterals.html

Convert CGFloat to NSNumber in Swift

In Swift 3.0

let myFloat : CGFloat = 1234.5

let myNumber = NSNumber(value: Float(myFloat))

or

let myNumber = NSNumber(value: Double(myFloat))

In Swift 2

let myNumber = NSNumber(double: myFloat.native)

or

let myNumber = NSNumber(double: Double(myFloat))

or

let myNumber = NSNumber(float: Float(myFloat))

What is most common and correct practice to get a CGFloat from an NSNumber?

This will get the correct result in any case:

 NSNumber *n = @42.42;
CGFloat cgf = [n doubleValue];

because CGFloat is either float or double.


NSNumber does not have a CGFloatValue method. You could define one
using the toll-free bridge to CFNumberRef:

@interface NSNumber (MyCGFloatValue)
-(CGFloat)myCGFloatValue;
@end

@implementation NSNumber (MyCGFloatValue)
-(CGFloat)myCGFloatValue{
CGFloat result;
CFNumberGetValue((__bridge CFNumberRef)(self), kCFNumberCGFloatType, &result);
return result;
}
@end

or using the C11 feature "Generic selection", where the compiler chooses the appropriate
code depending on the type of CGFloat:

@implementation NSNumber (MyCGFloatValue)
-(CGFloat)myCGFloatValue{
CGFloat result;
result = _Generic(result,
double: [self doubleValue],
float: [self floatValue]);
return result;
}
@end

And then

NSNumber *n = @42.24;
CGFloat f = [n myCGFloatValue];

but I doubt that it is worth the hassle.

How to get this value as a CGFloat?

In Swift:

let f = view.layer.valueForKeyPath("transform.rotation.z")!.floatValue

In Objective-C:

NSNumber* n = [self.layer valueForKeyPath:@"transform.rotation.z"];
CGFloat f = [n floatValue];

Swift equivalent of ceilf for CGFloat

The Swift way to do this now is to use rounded(.up) (or round(.up) if you want to change the variable in place). Behind the scenes it is using ceil, which can take a CGFloat as a parameter and is architecture independent.

let myCGFloat: CGFloat = 3.001
let y = myCGFloat.rounded(.up) // 4.0

This is equivalent to

var myCGFloat: CGFloat = 3.001
let y = ceil(myCGFloat) // 4.0

Any use of ceilf is no longer necessary.

See also my fuller answer on CGFloat with various rounding rules.

Is it safe to set a CGFloat to a double?

Remember that your code is compiled twice, once for 32-bit and once for 64-bit.

When compiling for 32-bit, your code looks like this:

float myDouble = 123.45;

So, the answer to your question is yes, the compiler knows how to handle that code and it will work just fine.

What you do need to watch for is the loss of precision when a 64-bit double value is assigned to a 32-bit float variable. In that case, the compiler will generate a warning for you about the loss of precision, but you will only see the warning when compiling the 32-bit version of your code.

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

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


Related Topics



Leave a reply



Submit