102

How can I round up a CGFloat in Swift? I've tried ceil(CDouble(myCGFloat)) but that only works on iPad Air & iPhone 5S.

When running on another simulated device I get an error saying 'NSNumber' is not a subtype of 'CGFloat'

Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
Oskar Persson
  • 6,605
  • 15
  • 63
  • 124
  • 2
    I think the reason it's working on iPad Air & iPhone 5S is because CGFloats are 64-bit on those architectures; on 32-bit architectures they're 32 bits. – Matt Gibson Jun 12 '14 at 10:36
  • I'm totally confused, becaus Swift tells me it has no idea what `floor` or `ceil` (or `floorf` or `ceilf`) is.. – Mark Reed Jan 06 '15 at 05:53
  • 2
    @MarkReed I know this was a while ago, but you need to add `import Foundation` – Ian Walter Jan 14 '15 at 03:42
  • For general rounding examples with `CGFloat` see [this answer](http://stackoverflow.com/a/34785079/3681880). – Suragch Jan 14 '16 at 08:49

7 Answers7

130

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
Matt Gibson
  • 37,886
  • 9
  • 99
  • 128
  • 4
    Horrible, but best solution so far. By the way, what's the difference between #if and if? – Oskar Persson Jun 12 '14 at 10:46
  • 4
    #if is used for conditional compilation. If you used a similar if statement, the offending code would still be compiled, and you'd still get the compile-time error, even though it wouldn't run at run time. This way whichever line of code that won't compile on the selected architecture is never actually sent for compilation. – Matt Gibson Jun 12 '14 at 10:48
  • my concern is about this solution is simply that it is a _compiler-time_ solution only, and not a _runtime_ solution. – holex Jun 12 '14 at 10:50
  • 1
    @holex Which is why it's fast. Also, the *problem* is a compile-time problem; it's because CGFloat is defined differently when compiling for different architectures. – Matt Gibson Jun 12 '14 at 10:55
  • 1
    (Though personally, I'd happily have seen this question left open for a lot longer to attract people with more solutions. I wonder if I can ask it again, only in a way not to have it immediately marked as a duplicate...) – Matt Gibson Jun 12 '14 at 11:00
  • @MattGibson, according to your warning about they are stored differently on dirrefent achitectures, I still don't see how this solution would work properly on an 64 bits architecture if you compiled the project for 32 bits architechture, or vica-verse. somehow the logic is broken here, because the two theories mutually exclude each other, or the problem is not such serious and that is just too much ado for nothing. – holex Jun 12 '14 at 11:03
  • If you compile for 64-bit architecture, CGFloat is a double, so `ceil` works fine. If you compile for 32-bit architecture, CGFloat is a float, so `ceilf` works fine. What's the problem you're seeing, exactly? (Would you like to open a new question about this? I'd like to see more discussion, but the comments here probably isn't the place.) – Matt Gibson Jun 12 '14 at 11:14
  • @MattGibson, I'm only trying to understand what you are selling here because the white gap is when the _runtime_ architecture is different from the _compiler-time_ architecture. according to what you just said, the application would work improperly in _runtime_ because the two architectures will be different. like the `CGFloat` is stored in e.g. a `float` in _runtime_ but the binary code would expect something else, because you already forced it in _compiling-time_ to work with e.g. `double` later in _runtime_ (or vica-verse). please, correct me if I'm wrong. – holex Jun 12 '14 at 11:28
  • @MattGibson, or in a nuthsell: in _compiling-time_ you don't know what the _runtime_ architechure will be; and in my view making such decisions in _compiling-time_ which depends on the _runtime_ achitechture/environment, not a good practice or advice, even it would be faster. sorry for off the thread. – holex Jun 12 '14 at 11:32
  • No, no, this is a perfectly good debate to be having. To get some more opinions, I've [created a new question](http://stackoverflow.com/questions/24184810/should-conditional-compilation-be-used-to-cope-with-difference-in-cgfloat-on-dif), specifically asking about my solution here. Come join in! :) – Matt Gibson Jun 12 '14 at 12:49
  • 2
    @holex: Whether CGFloat is float or double in the compiled binary is completely determined at *compile time*. In a 32-bit binary (which can run on 32-bit and 64-bit hardware), CGFloat is a float. In a 64-bit binary (which can only run on 64-bit hardware), CGFloat is a double. - A *Universal binary* contains both a 32-bit and a 64-bit binary, and picks the most appropriate for the hardware *at runtime* for execution. - So for this question (whether to call ceil() or ceilf()) it is only relevant if the code is *compiled* as 32-bit or 64-bit. – Martin R Jun 12 '14 at 13:38
59

With Swift 5, you can choose one of the 3 following paths in order to round up a CGFloat.


#1. Using CGFloat's rounded(_:) method

FloatingPoint protocol gives types that conform to it a rounded(_:) method. CGFloat's rounded(_:) has the following declaration:

func rounded(_ rule: FloatingPointRoundingRule) -> CGFloat

Returns this value rounded to an integral value using the specified rounding rule.

The Playground sample code below shows how to use rounded(_:) in order to round up a CGFloat value:

import CoreGraphics

let value1: CGFloat = -0.4
let value2: CGFloat = -0.5
let value3: CGFloat = -1
let value4: CGFloat = 0.4
let value5: CGFloat = 0.5
let value6: CGFloat = 1

let roundedValue1 = value1.rounded(.up)
let roundedValue2 = value2.rounded(.up)
let roundedValue3 = value3.rounded(.up)
let roundedValue4 = value4.rounded(.up)
let roundedValue5 = value5.rounded(.up)
let roundedValue6 = value6.rounded(.up)

print(roundedValue1) // prints -0.0
print(roundedValue2) // prints -0.0
print(roundedValue3) // prints -1.0
print(roundedValue4) // prints 1.0
print(roundedValue5) // prints 1.0
print(roundedValue6) // prints 1.0

#2. Using ceil(_:) function

Darwin provides a ceil(_:) function that has the following declaration:

func ceil<T>(_ x: T) -> T where T : FloatingPoint

The Playground code below shows how to use ceil(_:) in order to round up a CGFloat value:

import CoreGraphics

let value1: CGFloat = -0.4
let value2: CGFloat = -0.5
let value3: CGFloat = -1
let value4: CGFloat = 0.4
let value5: CGFloat = 0.5
let value6: CGFloat = 1

let roundedValue1 = ceil(value1)
let roundedValue2 = ceil(value2)
let roundedValue3 = ceil(value3)
let roundedValue4 = ceil(value4)
let roundedValue5 = ceil(value5)
let roundedValue6 = ceil(value6)

print(roundedValue1) // prints -0.0
print(roundedValue2) // prints -0.0
print(roundedValue3) // prints -1.0
print(roundedValue4) // prints 1.0
print(roundedValue5) // prints 1.0
print(roundedValue6) // prints 1.0

#3. Using NumberFormatter

If you want to round up a CGFloat and format it with style in the same operation, you may use NumberFormatter.

import Foundation
import CoreGraphics

let value1: CGFloat = -0.4
let value2: CGFloat = -0.5
let value3: CGFloat = -1
let value4: CGFloat = 0.4
let value5: CGFloat = 0.5
let value6: CGFloat = 1

let formatter = NumberFormatter()
formatter.numberStyle = NumberFormatter.Style.decimal
formatter.roundingMode = NumberFormatter.RoundingMode.ceiling
formatter.maximumFractionDigits = 0

let roundedValue1 = formatter.string(for: value1)
let roundedValue2 = formatter.string(for: value2)
let roundedValue3 = formatter.string(for: value3)
let roundedValue4 = formatter.string(for: value4)
let roundedValue5 = formatter.string(for: value5)
let roundedValue6 = formatter.string(for: value6)

print(String(describing: roundedValue1)) // prints Optional("-0")
print(String(describing: roundedValue2)) // prints Optional("-0")
print(String(describing: roundedValue3)) // prints Optional("-1")
print(String(describing: roundedValue4)) // prints Optional("1")
print(String(describing: roundedValue5)) // prints Optional("1")
print(String(describing: roundedValue6)) // prints Optional("1")
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
31

Use it on swift 5

let x = 6.5

// Equivalent to the C 'round' function:
print(x.rounded(.toNearestOrAwayFromZero))
// Prints "7.0"

// Equivalent to the C 'trunc' function:
print(x.rounded(.towardZero))
// Prints "6.0"

// Equivalent to the C 'ceil' function:
print(x.rounded(.up))
// Prints "7.0"

// Equivalent to the C 'floor' function:
print(x.rounded(.down))
// Prints "6.0"
mohsen
  • 4,698
  • 1
  • 33
  • 54
22

The most correct syntax would probably be:

var f: CGFloat = 2.5
var roundedF = CGFloat(ceil(Double(f)))

To use ceil I will first make the CGFloat a Double and after ceiling, I convert it back to CGFloat.

That works when CGFloat is defined either as CFloat or CDouble.

You could also define a ceil for floats (This has been actually implemented in Swift 2):

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

Then you will be able to call directly

var roundedF: CGFloat = ceil(f)

while preserving type safety.

I actually believe this should be the solution chosen by Apple, instead of having separate ceil and ceilf functions because they don't make sense in Swift.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Works, but to me it seems like Matt's solution is faster. More messy, but faster. – Oskar Persson Jun 12 '14 at 12:09
  • @Oskar Check the 2nd solution, I have just added. – Sulthan Jun 12 '14 at 12:12
  • With the 2nd solution I still get the error message: `'NSNumber' is not a subtype of 'CGFloat'` – Oskar Persson Jun 12 '14 at 12:18
  • Just the function you gave me with `var f: CGFloat = 2.5`. Try building that for an iPhone 5S. – Oskar Persson Jun 12 '14 at 12:21
  • @Oskar I am building it without problems. The literal type 2.5 is correctly inferred as `CGFloat`. – Sulthan Jun 12 '14 at 12:26
  • 1
    This (Sulthan's first formulation) is the right answer. I don't see why it isn't the accepted one. I give exactly the same formulation (independently) here: http://stackoverflow.com/a/24186312/341994 I don't see why this is "messy", and certainly not why it is messier than Matt Gibson's answer. The fact is that it is Swift that is messy. None of this should be necessary; we need intelligent automatic casting of numeric types. File enhancement requests, everybody. – matt Jun 12 '14 at 14:05
  • @matt He didn't say it's messier, he saied it's slower :) I am not sure why the 2nd part of my answer doesn't work for the OP. It seems to be the best solution for me. – Sulthan Jun 12 '14 at 14:15
  • 1
    I put your solution in CGFloat's extension. Worked well! extension CGFloat { func roundDown() -> CGFloat { return CGFloat(floor(Double(self))) } func roundUp() -> CGFloat { return CGFloat(ceil(Double(self))) } } Now, I can call myFloat.roundDown() or myFloat.roundUp()! Thanks, man – Thomás Pereira Mar 11 '15 at 19:55
10

from Swift Standard Library you can round it in-place as well:

var value: CGFloat = -5.7
value.round(.up) // -5.0
holex
  • 23,961
  • 7
  • 62
  • 76
6

Building off of holex's answer. I did

func accurateRound(value: Double) -> Int {

            var d : Double = value - Double(Int(value))

            if d < 0.5 {
                return Int(value)
            } else {
                return Int(value) + 1
            }
        }

-edit extension edition-

I also recently turned this into an extension for Floats thought I'd share as well :)

extension Float {
    func roundToInt() -> Int{
        var value = Int(self)
        var f = self - Float(value)
        if f < 0.5{
            return value
        } else {
            return value + 1
        }
    }
}

This is makes it so you can just be like

var f : Float = 3.3
f.roundToInt()
Krtko
  • 1,055
  • 18
  • 24
4

Use rounded method.

Demo (swift 5.5.2) :

CGFloat(5.0).rounded(.up) // -> 5

CGFloat(5.1).rounded(.up) // -> 6
CGFloat(5.2).rounded(.up) // -> 6
CGFloat(5.3).rounded(.up) // -> 6
CGFloat(5.4).rounded(.up) // -> 6
CGFloat(5.5).rounded(.up) // -> 6
CGFloat(5.6).rounded(.up) // -> 6
CGFloat(5.7).rounded(.up) // -> 6
CGFloat(5.8).rounded(.up) // -> 6
CGFloat(5.9).rounded(.up) // -> 6
frouo
  • 5,087
  • 3
  • 26
  • 29