Окружте CGFloat у Свіфті


78

Як я можу округлити CGFloat в Swift? Я пробував, ceil(CDouble(myCGFloat))але це працює лише на iPad Air та iPhone 5S.

Під час роботи на іншому імітованому пристрої з'являється повідомлення про помилку 'NSNumber' is not a subtype of 'CGFloat'


2
Я думаю, причина, по якій вона працює на iPad Air та iPhone 5S, полягає в тому, що CGFloats є 64-розрядними для цих архітектур; для 32-розрядних архітектур вони складають 32 біти.
Метт Гібсон,

Я повністю сплутати, becaus Swift каже мені , що це не має ні найменшого уявлення , що floorі ceil(або floorfчи ceilf) є ..
Марк Рід

2
@MarkReed Я знаю, що це було деякий час тому, але потрібно додатиimport Foundation
Ian Walter

Загальні приклади округлення CGFloatдив. У цій відповіді .
Сурагч

Відповіді:


117

Оновлення : Apple тепер визначила деякі версії загальних функцій, характерних для CGFloat, таких як ceil:

func ceil(x: CGFloat) -> CGFloat

... зокрема, щоб впоратися з 32/64-бітною різницею. Якщо ви просто використовуєте ceilаргумент CGFloat, він тепер повинен працювати на всіх архітектурах.

Моя оригінальна відповідь:

Я думаю, це досить жахливо, але чи може хтось придумати кращий спосіб? #ifздається, не працює CGFLOAT_IS_DOUBLE; Я думаю, ви обмежені у створенні конфігурацій, з того, що я бачу в документації для умовної компіляції .

var x = CGFloat(0.5)

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

4
Жахливе, але найкраще рішення поки що. До речі, яка різниця між #if та if?
Оскар Перссон

4
#if використовується для умовної компіляції. Якби ви використовували подібний оператор if, код, що порушує, все одно буде скомпільований, і ви все одно отримаєте помилку під час компіляції, навіть якщо вона не запускається під час виконання. Таким чином, будь-який рядок коду, який не компілюється у вибраній архітектурі, ніколи фактично не надсилається для компіляції.
Метт Гібсон,

1
@holex Ось чому це швидко. Крім того, проблема полягає в проблемі під час компіляції; це тому, що CGFloat визначається по-різному при компіляції для різних архітектур.
Метт Гібсон,

1
(Хоча особисто я б із задоволенням бачив, що це питання залишається відкритим набагато довше, щоб залучити людей із більшою кількістю рішень. Цікаво, чи можу я поставити його ще раз, лише таким чином, щоб його відразу не позначили як дублікат ... )
Метт Гібсон,

2
@holex: Чи є CGFloat плаваючим чи подвійним у скомпільованому двійковому файлі, повністю визначається під час компіляції . У 32-розрядному двійковому файлі (який може працювати на 32-розрядному та 64-розрядному обладнанні), CGFloat є плаваючим. У 64-розрядному двійковому файлі (який може працювати лише на 64-розрядному обладнанні), CGFloat є подвійним. - Універсальний двійковий файл містить як 32-розрядний, так і 64-розрядний двійковий файл, і вибирає найбільш підходяще для апаратного забезпечення під час виконання для виконання. - Отже, для цього питання (чи слід викликати ceil () або ceilf ()), це актуально лише в тому випадку, якщо код складений як 32-розрядний або 64-розрядний.
Martin R

40

За допомогою Swift 5 ви можете вибрати один із 3 наступних шляхів , щоб округлити a CGFloat.


№1. Використовуючи CGFloat'srounded(_:) методу

FloatingPointпротокол дає типи, що відповідають йому rounded(_:)методом. CGFloat«S rounded(_:)має наступну заяву:

func rounded(_ rule: FloatingPointRoundingRule) -> CGFloat

Повертає це значення, округлене до інтегрального значення, використовуючи вказане правило округлення.

Наведений нижче приклад коду майданчика показує, як використовувати rounded(_:)для округлення CGFloatзначення:

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. Використовуючиceil(_:) функції

Дарвін надає ceil(_:)функцію, яка має таке оголошення:

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

Наведений нижче код майданчика показує, як використовувати ceil(_:)для округлення CGFloatзначення:

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. ВикористовуючиNumberFormatter

Якщо ви хочете округлити a CGFloatта відформатувати його зі стилем за тією ж операцією, ви можете використовувати 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")

21

Найбільш правильним синтаксисом був би:

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

Для використання ceilя спочатку зроблю CGFloatа, Doubleа після стелі, я перетворюю його назад уCGFloat .

Це працює, коли CGFloatвизначається як CFloatабоCDouble .

Ви також можете визначити a ceilдля floats ( Це насправді було реалізовано в Swift 2 ):

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

Тоді ви зможете зателефонувати безпосередньо

var roundedF: CGFloat = ceil(f)

зберігаючи безпеку типу.

Я насправді вважаю, що це має бути рішення, яке обрало Apple, замість того, щоб мати окремі функції ceilта ceilfфункції, оскільки вони не мають сенсу в Swift.


Працює, але мені здається, що рішення Метта швидше. Більш брудний, але швидший.
Оскар Перссон

@Oskar Перевірте друге рішення, яке я щойно додав.
Султан

З 2-м рішенням я все ще отримую повідомлення про помилку:'NSNumber' is not a subtype of 'CGFloat'
Оскар Перссон

1
Це (перша формулювання Султана) - правильна відповідь. Я не розумію, чому це не прийнятий. Я даю точно таку ж формулу (незалежно) тут: stackoverflow.com/a/24186312/341994. Я не розумію, чому це "брудно", і, звичайно, не тому, чому це сумніше, ніж відповідь Метта Гібсона. Справа в тому, що саме Свіфт безладний. Нічого з цього не повинно бути необхідним; нам потрібен інтелектуальний автоматичний привід числових типів. Подайте запити на вдосконалення всім.
matt

1
Я розмістив ваше рішення в розширенні CGFloat. Добре працювали! розширення CGFloat {func roundDown () -> CGFloat {return CGFloat (floor (Double (self)))} func roundUp () -> CGFloat {return CGFloat (ceil (Double (self)))}} Тепер я можу зателефонувати myFloat .roundDown () або myFloat.roundUp ()! Дякую, чоловіче
Томас Калмон

12

Використовуйте його швидко 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"

7

зі стандартної бібліотеки Swift ви також можете округлити її на місці:

var value: CGFloat = -5.7
value.round(.up) // -5.0

1
println("\(roundUp(Double(180.0)))") //prints 181, should print 180
Оскар Перссон

Ви абсолютно праві, я виправив першу гілку.
holex

Як отримати десяткове значення 0,7 із заданого CGFloat -5,7?
Кіран

Ви хотіли б, щоб я навів формулу, як (-5.0) - (-5.7) = 0.7чи щось?
holex

5

Будуючи відповідь holex. я зробила

func accurateRound(value: Double) -> Int {

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

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

-редагувати розширення

Я також нещодавно перетворив це на розширення для Floats, про яке я також поділюсь :)

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

Це робить це, щоб ви могли просто бути подібними

var f : Float = 3.3
f.roundToInt()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.