103

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'

4
  • 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. Commented Jun 12, 2014 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
    Commented Jan 6, 2015 at 5:53
  • 2
    @MarkReed I know this was a while ago, but you need to add import Foundation
    – Ian Walter
    Commented Jan 14, 2015 at 3:42
  • For general rounding examples with CGFloat see this answer.
    – Suragch
    Commented Jan 14, 2016 at 8:49

7 Answers 7

132

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
11
  • 4
    Horrible, but best solution so far. By the way, what's the difference between #if and if? Commented Jun 12, 2014 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. Commented Jun 12, 2014 at 10:48
  • 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. Commented Jun 12, 2014 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...) Commented Jun 12, 2014 at 11:00
  • 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
    Commented Jun 12, 2014 at 13:38
60

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")
1
  • golden (standard of an) answer
    – user5306470
    Commented Jul 21, 2022 at 1:53
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"
1
  • 1
    Thank you for the clear explinations of the enum choices!
    – tyirvine
    Commented Jul 28, 2021 at 4:42
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.

8
  • Works, but to me it seems like Matt's solution is faster. More messy, but faster. Commented Jun 12, 2014 at 12:09
  • @Oskar Check the 2nd solution, I have just added.
    – Sulthan
    Commented Jun 12, 2014 at 12:12
  • With the 2nd solution I still get the error message: 'NSNumber' is not a subtype of 'CGFloat' Commented Jun 12, 2014 at 12:18
  • 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: 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
    Commented Jun 12, 2014 at 14:05
  • 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 Commented Mar 11, 2015 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
4
  • 1
    println("\(roundUp(Double(180.0)))") //prints 181, should print 180 Commented Jun 12, 2014 at 10:31
  • you are totally right, I've corrected the first branch.
    – holex
    Commented Jun 12, 2014 at 10:36
  • How to get decimal value 0.7 from given CGFloat -5.7?
    – kiran
    Commented Jan 20, 2020 at 18:40
  • would you want me to provide a formula like (-5.0) - (-5.7) = 0.7 or something?
    – holex
    Commented Jan 21, 2020 at 9:12
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()
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
1
  • This should be the accepted answer
    – Helix
    Commented Mar 25, 2022 at 14:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.