Runden Sie einen CGFloat in Swift ab

78

Wie kann ich einen CGFloat in Swift aufrunden? Ich habe es versucht, ceil(CDouble(myCGFloat))aber das funktioniert nur auf iPad Air & iPhone 5S.

Wenn ich auf einem anderen simulierten Gerät laufe, wird eine Fehlermeldung angezeigt 'NSNumber' is not a subtype of 'CGFloat'

Oskar Persson
quelle
2
Ich denke, der Grund, warum es auf iPad Air und iPhone 5S funktioniert, ist, dass CGFloats auf diesen Architekturen 64-Bit sind. Auf 32-Bit-Architekturen sind es 32 Bit.
Matt Gibson
Ich bin total verwirrt, weil Swift mir sagt, dass es keine Ahnung hat, was flooroder ceil(oder floorfoder ceilf) ist.
Mark Reed
2
@ MarkReed Ich weiß, dass dies vor einer Weile war, aber Sie müssen hinzufügenimport Foundation
Ian Walter
Allgemeine Rundungsbeispiele finden CGFloatSie in dieser Antwort .
Suragch

Antworten:

117

Update : Apple hat jetzt einige CGFloat-spezifische Versionen allgemeiner Funktionen definiert, wie ceil:

func ceil(x: CGFloat) -> CGFloat

... speziell um den 32/64-Bit-Unterschied zu bewältigen. Wenn Sie einfach ceilein CGFloat-Argument verwenden, sollte es jetzt auf allen Architekturen funktionieren.

Meine ursprüngliche Antwort:

Das ist ziemlich schrecklich, denke ich, aber kann sich jemand einen besseren Weg vorstellen? #ifscheint nicht zu funktionieren CGFLOAT_IS_DOUBLE; Ich denke, Sie können nur Konfigurationen erstellen, wie ich in der Dokumentation zur bedingten Kompilierung sehen kann .

var x = CGFloat(0.5)

#if arch(x86_64) || arch(arm64)
var test = ceil(x)
#else
var test = ceilf(x)
#endif
Matt Gibson
quelle
4
Schreckliche, aber bisher beste Lösung. Was ist übrigens der Unterschied zwischen #if und if?
Oskar Persson
4
#if wird für die bedingte Kompilierung verwendet. Wenn Sie eine ähnliche if-Anweisung verwenden, wird der fehlerhafte Code weiterhin kompiliert, und Sie erhalten weiterhin den Fehler zur Kompilierungszeit, auch wenn er zur Laufzeit nicht ausgeführt wird. Auf diese Weise wird die Codezeile, die in der ausgewählten Architektur nicht kompiliert wird, niemals zur Kompilierung gesendet.
Matt Gibson
1
@holex Deshalb ist es schnell. Das Problem ist auch ein Problem zur Kompilierungszeit. Dies liegt daran, dass CGFloat beim Kompilieren für verschiedene Architekturen unterschiedlich definiert ist.
Matt Gibson
1
(Obwohl ich persönlich gerne gesehen hätte, dass diese Frage viel länger offen blieb, um Menschen mit mehr Lösungen anzulocken. Ich frage mich, ob ich sie noch einmal stellen kann, nur so, dass sie nicht sofort als Duplikat markiert wird ... )
Matt Gibson
2
@holex: Ob CGFloat in der kompilierten Binärdatei float oder double ist, wird zur Kompilierungszeit vollständig bestimmt . In einer 32-Bit-Binärdatei (die auf 32-Bit- und 64-Bit-Hardware ausgeführt werden kann) ist CGFloat ein Float. In einer 64-Bit-Binärdatei (die nur auf 64-Bit-Hardware ausgeführt werden kann) ist CGFloat ein Double. - Eine Universal-Binärdatei enthält sowohl eine 32-Bit- als auch eine 64-Bit-Binärdatei und wählt zur Laufzeit die für die Hardware am besten geeignete zur Ausführung aus. - Für diese Frage (ob Ceil () oder Ceilf () aufgerufen werden soll) ist dies nur relevant, wenn der Code als 32-Bit oder 64-Bit kompiliert ist .
Martin R
40

Mit Swift 5 können Sie einen der drei folgenden Pfade auswählen , um a aufzurunden CGFloat.


# 1. Mit CGFloatder rounded(_:)Methode von

FloatingPointProtokoll gibt Typen, die ihm entsprechen, eine rounded(_:)Methode. CGFloat‚s rounded(_:)hat die folgende Erklärung ab :

func rounded(_ rule: FloatingPointRoundingRule) -> CGFloat

Gibt diesen Wert unter Verwendung der angegebenen Rundungsregel auf einen ganzzahligen Wert gerundet zurück.

Der folgende Spielplatz-Beispielcode zeigt, wie Sie rounded(_:)einen CGFloatWert aufrunden:

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. Mit ceil(_:)Funktion

Darwin bietet eine ceil(_:)Funktion mit der folgenden Deklaration:

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

Der folgende Spielplatzcode zeigt, wie Sie ceil(_:)einen CGFloatWert aufrunden:

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. Verwenden vonNumberFormatter

Wenn Sie a aufrunden CGFloatund im selben Vorgang mit Stil formatieren möchten , können Sie verwenden 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
quelle
21

Die korrekteste Syntax wäre wahrscheinlich:

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

Zur Verwendung ceilmache ich zuerst die CGFloata Doubleund nach der Decke konvertiere ich sie zurück zu CGFloat.

Das funktioniert, wenn CGFloatentweder als CFloatoder definiert ist CDouble.

Sie können auch ein ceilfor float definieren ( Dies wurde tatsächlich in Swift 2 implementiert ):

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

Dann können Sie direkt anrufen

var roundedF: CGFloat = ceil(f)

unter Wahrung der Typensicherheit.

Ich glaube , eigentlich soll dies die Lösung von Apple gewählt werden, statt mit getrennten ceilund ceilfFunktionen , weil sie keinen Sinn in Swift machen.

Sulthan
quelle
Funktioniert, aber für mich scheint Matts Lösung schneller zu sein. Unordentlicher, aber schneller.
Oskar Persson
@Oskar Überprüfen Sie die 2. Lösung, die ich gerade hinzugefügt habe.
Sulthan
Mit der 2. Lösung erhalte ich immer noch die Fehlermeldung:'NSNumber' is not a subtype of 'CGFloat'
Oskar Persson
1
Dies (Sulthans erste Formulierung) ist die richtige Antwort. Ich verstehe nicht, warum es nicht akzeptiert wird. Ich gebe hier genau die gleiche Formulierung (unabhängig): stackoverflow.com/a/24186312/341994 Ich verstehe nicht, warum dies "chaotisch" ist und schon gar nicht, warum es chaotischer ist als Matt Gibsons Antwort. Tatsache ist, dass es Swift ist, der chaotisch ist. Nichts davon sollte notwendig sein; Wir brauchen intelligentes automatisches Casting numerischer Typen. Anfragen zur Dateierweiterung an alle.
Matt
1
Ich habe Ihre Lösung in die Erweiterung von CGFloat aufgenommen. Gut gearbeitet! Erweiterung CGFloat {func roundDown () -> CGFloat {CGFloat zurückgeben (Etage (Double (self)))} func roundUp () -> CGFloat {CGFloat zurückgeben (Ceil (Double (self)))}} Jetzt kann ich myFloat aufrufen .roundDown () oder myFloat.roundUp ()! Danke, Mann
Thomás Calmon
12

Verwenden Sie es auf 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
quelle
7

In der Swift Standard Library können Sie es auch direkt abrunden :

var value: CGFloat = -5.7
value.round(.up) // -5.0
holex
quelle
1
println("\(roundUp(Double(180.0)))") //prints 181, should print 180
Oskar Persson
Sie haben vollkommen recht, ich habe den ersten Zweig korrigiert.
Holex
Wie bekomme ich einen Dezimalwert von 0,7 aus einem gegebenen CGFloat -5,7?
Kiran
Möchten Sie, dass ich eine Formel wie (-5.0) - (-5.7) = 0.7oder so bereitstelle ?
Holex
5

Aufbauend auf der Antwort von holex. Ich tat

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 Erweiterung Edition-

Ich habe dies kürzlich auch in eine Erweiterung für Floats verwandelt, von der ich dachte, ich würde sie auch teilen :)

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

Dies macht es so, dass Sie einfach so sein können

var f : Float = 3.3
f.roundToInt()
Krtko
quelle