Skip to content

Instantly share code, notes, and snippets.

@indragiek
Created January 29, 2015 02:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save indragiek/6b500e84927f0be9c2ae to your computer and use it in GitHub Desktop.
Save indragiek/6b500e84927f0be9c2ae to your computer and use it in GitHub Desktop.
Swift bug when formatting CGFloats
import Cocoa
// Doesn't work for CGFloats
let a: CGFloat = 1.0
let b: CGFloat = 2.0
String(format: "%f %f", a, b) // "0.000000 0.000000"
// Works for Floats
let c: Float = 1.0
let d: Float = 2.0
String(format: "%f %f", c, d) // "1.000000 2.000000"
// Works for Doubles
let e: Double = 1.0
let f: Double = 2.0
String(format: "%f %f", e, f) // "1.000000 2.000000"
Copy link

ghost commented Jan 29, 2015

I’m assuming that initialiser is actually NSString’s, whose signature is

convenience init(format: NSString, _ args: CVarArgType...)

It’s interesting that CGFloat ends up being converted to CVarArgType even though it doesn’t explicitly conform to CVarArgType. For example,

a.encode()

is a compiler error whereas

c.encode()

returns a word array representation suitable for variadic arguments.

Since CGFloat is not explicitly CVarArgType, there is some conversion path that results in CVarArgType. I am tempted to say it is a conversion to NSNumber because of the code below (appended to your original code):

func format1(value: CVarArgType) -> String {
    return String(format: "%f", value)
}

func format2(value: CVarArgType) -> String {
    return String(format: "%@", value)
}

format1(a)    // "0.000000"
format2(a)    // "1"

format1(c)    // "1.000000"
format2(c)    // "(null)"

format1(NSNumber(double: 1.0))    // "0.000000"
format2(NSNumber(double: 1.0))    // "1"

@indragiek
Copy link
Author

@bavarious Yep, it is using the NSString initializer. The public interface for Swift's String does not have that method defined on it, it just uses the bridging. Here's another interesting bit:

// The following code crashes
let numbers = [a, b]
String(format: "%f %f", arguments: numbers)

(This is using the version of the initializer that allows passing the arguments as an array as opposed to a variadic argument list)

@natecook1000
Copy link

Swift's String is extended in Foundation to include most of NSString's initializers—you can see all of them by command-clicking on an import Foundation statement or on the SwiftDoc.org String page marked with [Foundation].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment