it-swarm.com.de

In Uift (und umgekehrt) Bit Casting in Swift

Ich suche nach einem direkten Weg, die Bitwerte eines Int in UInt zu konvertieren und umgekehrt. Zum Beispiel (zur Vereinfachung der 8-Bit-Ganzzahl) möchte ich Folgendes erreichen:

let unsigned: UInt8 = toUInt8(-1)  // unsigned is 255 or 0xff
let signed:   Int8  = toInt8(0xff) // signed is -1 

Zuerst kam ich mit der folgenden Lösung heraus:

let unsigned = unsafeBitCast(Int8(-1), UInt8.self)
let signed   = unsafeBitCast(UInt8(0xff), Int8.self)

In der Dokumentation "unsafeBitCast ()" heißt es jedoch:

.. Achtung: Bricht die Garantien des Swift-Typsystems ab; benutzen mit extreme Sorgfalt. Es gibt fast immer eine bessere Möglichkeit, etwas zu tun.

Hat jemand den besseren Weg?

20

Du kannst tun:

let unsigned = UInt8(bitPattern: Int8(-1)) // -> 255
let signed   = Int8(bitPattern: UInt8(0xff)) // -> -1

Es gibt viele ähnliche Initialisierer:

extension Int8 {
    init(_ v: UInt8)
    init(_ v: UInt16)
    init(truncatingBitPattern: UInt16)
    init(_ v: Int16)
    init(truncatingBitPattern: Int16)
    init(_ v: UInt32)
    init(truncatingBitPattern: UInt32)
    init(_ v: Int32)
    init(truncatingBitPattern: Int32)
    init(_ v: UInt64)
    init(truncatingBitPattern: UInt64)
    init(_ v: Int64)
    init(truncatingBitPattern: Int64)
    init(_ v: UInt)
    init(truncatingBitPattern: UInt)
    init(_ v: Int)
    init(truncatingBitPattern: Int)
    init(bitPattern: UInt8)
}
36
rintaro

Ich nahm die Algebra-Route. Das Testen hat sich als schmerzhaft erwiesen, da es leicht ist, einen Überlauf zu bekommen, da die starke Typisierung die Ausführung unterbricht. PlayGround hat einen negativen Wert aus der toUInt-Funktion zurückgegeben, der abstürzte oder lustige Fehler beim doppelten Casting ergab (ich habe einen Fehlerbericht geöffnet). Jedenfalls bin ich damit fertig geworden:

func toUint(signed: Int) -> UInt {

    let unsigned = signed >= 0 ?
        UInt(signed) :
        UInt(signed  - Int.min) + UInt(Int.max) + 1

    return unsigned
}

func toInt(unsigned: UInt) -> Int {

    let signed = (unsigned <= UInt(Int.max)) ?
        Int(unsigned) :
        Int(unsigned - UInt(Int.max) - 1) + Int.min

    return signed
}

Ich habe sie mit allen extremen Werten getestet (UInt.min, UInt.max, Int.min, Int.max) und wenn XCode nicht verrückt wird, scheint es zu funktionieren, aber es sieht zu kompliziert aus. Bizarr genug kann das UInt-zu-Int-Bit-Casting einfach mit der Eigenschaft hashvalue erreicht werden, wie in:

signed = UInt.max.hashValue // signed is -1

Natürlich ist es nicht garantiert, immer zu arbeiten (sollte es tun, aber ich möchte lieber nicht die Chance nutzen). 

Jede andere Idee wird geschätzt.

2

numericCast(...) ist was Sie suchen. Es ist ein Satz generischer Funktionen, die von und zu verschiedenen Nummernarten konvertiert werden. Sie wählt die korrekte Implementierung basierend auf den Typen ihres Arguments und dem Rückgabetyp aus.

0
stigi