Как создать общую функцию integer-to-hex для всех типов Integer?

Я хочу создать функцию Integer-Hex для всех целых типов.

Для 1-байтового Int8 он возвращает две буквы, например 0A

Для 2-байтового Int16 он возвращает четыре буквы, например 0A0B

для 8-байтового Int64, он возвращает 16 букв, например, 0102030405060708

func hex(v: Int) -> String {
    var s = ""
    var i = v
    for _ in 0..<sizeof(Int)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

func hex(v: Int64) -> String {
    var s = ""
    var i = v
    for _ in 0..<sizeof(Int64)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

func hex(v: Int32) -> String {
    var s = ""
    var i = v
    for _ in 0..<sizeof(Int32)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

func hex(v: Int16) -> String {
    var s = ""
    var i = v
    for _ in 0..<sizeof(Int16)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

func hex(v: Int8) -> String {
    var s = ""
    var i = v
    for _ in 0..<sizeof(Int8)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

Приведенный выше код работает нормально.

Затем я попытался создать такую ​​общую версию:

func hex<T: IntegerType>(v: T) -> String {
    var s = ""
    var i = v
    for _ in 0..<sizeof(T)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

При компиляции этого кода я получил ошибку: T не конвертируется в Int

Каков правильный способ достижения этой задачи?

Ответ 1

Очень простое решение состоит в объединении входного значения в IntMax с помощью .toIntMax().:

func hex<T: IntegerType>(v: T) -> String {
    var s = ""
    var i = v.toIntMax()
    for _ in 0..<sizeof(T)*2 {
        s = String(format: "%x", i & 0xF) + s
        i >>= 4
    }
    return s
}

Примечание. Это работает только с значениями 0...Int64.max.


Но я бы сделал:

func hex<T: IntegerType>(v: T) -> String {
    return String(format:"%0\(sizeof(T) * 2)x", v.toIntMax())
}

Примечание. Это работает только с значениями 0...UInt32.max.


Добавлено:. Это работает со всеми доступными целыми типами/значениями.

func hex<T:IntegerType>(var v:T) -> String {
    var s = ""
    for _ in 0..<sizeof(T) * 2 {
        s = String(format: "%X", (v & 0xf).toIntMax()) + s
        v /= 16
    }
    return s
}
  • .toIntMax(), чтобы придать T конкретному целочисленному типу.
  • / 16 вместо >> 4.

Ответ 2

Из вашего вопроса неясно, почему вы не используете встроенный инициализатор, который уже делает это для вас:

let i = // some kind of integer
var s = String(i, radix:16)

Если вам не нравится результирующий формат s, его намного проще загладить и добавить его лишними символами, чем пройти всю работу, которую вы здесь делаете.

Ответ 3

Проблема заключается в том, что, хотя >> определен для всех целых типов, IntegerType не гарантирует его наличия. IntegerType соответствует IntegerArithmeticType, что дает вам +, - и т.д. и BitwiseOperationsType, что дает вам &, | и т.д. Но это не выглядит так: >> is в любом из них.

Бит бэджа, но вы можете расширить целые числа с помощью нового протокола, скажем Shiftable, а затем потребовать, чтобы:

protocol Shiftable {
    func >>(lhs: Self, rhs: Self) -> Self
    // + other shifting operators
}

extension Int: Shiftable {
  // nothing actually needed here
}

extension Int16: Shiftable { } // etc

// still need IntegerType if you want to do other operations
// (or alternatively Shiftable could require IntegerType conformance)
func shiftIt<I: protocol<IntegerType, Shiftable>>(i: I) {
    println(i+1 >> 4)
}

shiftIt(5000)
shiftIt(5000 as Int16)

edit: oop, похоже на подобные проблемы с String(format: ...), вот лучшее, что я мог придумать:

edit2: как @rintaro ponts .toIntMax() - это более простое решение для этого, но это любопытное удовольствие от выяснения того, как заставить его работать полностью в целом:-)

func hex<T: protocol<IntegerType,Shiftable>>(v: T) -> String {

    // In creating this dictionary, the IntegerLiterals should
    // be converted to type T, which means you can use a type
    // T to look them up.  Hopefully the optimizer will only
    // run this code once per version of this function...
    let hexVals: [T:Character] = [
        0:"0", 1:"1", 2:"2", 3:"3", 4:"4",
        5:"5", 6:"6", 7:"7", 8:"8", 9:"9",
        10:"A", 11:"B", 12:"C", 13:"D", 14:"E", 15:"F"
    ]

    var chars: [Character] = []
    var i = v
    for _ in 0..<sizeof(T)*2 {
        chars.append(hexVals[(i & 0xF)] ?? "?")
        i = i >> 4
    }
    return String(lazy(chars).reverse())
}

Ответ 4

Спасибо всем за ввод.

Первая версия общих функций, которые я создал, была:

func hex<T: UnsignedIntegerType>(v: T) -> String {
    var fmt = "%0\(sizeof(T)*2)"
    fmt += (sizeof(T) > 4) ? "llx" : "x"
    return String(format: fmt, v.toUIntMax())
}

func hex<T: SignedIntegerType>(v: T) -> String {
    var fmt = "%0\(sizeof(T)*2)"
    fmt += (sizeof(T) > 4) ? "llx" : "x"
    return String(format: fmt, v.toIntMax())
}

Я использовал следующий код для проверки двух функций

println("=== 64-bit ===")
println(hex(UInt64.max))
println(hex(UInt64.min))
println(hex(Int64.max))
println(hex(Int64.min))

println("=== 32-bit ===")
println(hex(UInt32.max))
println(hex(UInt32.min))
println(hex(Int32.max))
println(hex(Int32.min))

println("=== 16-bit ===")
println(hex(UInt16.max))
println(hex(UInt16.min))
println(hex(Int16.max))
println(hex(Int16.min))

println("=== 8-bit ===")
println(hex(UInt8.max))
println(hex(UInt8.min))
println(hex(Int8.max))
println(hex(Int8.min))

Выход для 16-битных и 8-битных отрицательных целых чисел, по-видимому, ошибочен.

=== 64-bit ===
ffffffffffffffff
0000000000000000
7fffffffffffffff
8000000000000000
=== 32-bit ===
ffffffff
00000000
7fffffff
80000000
=== 16-bit ===
ffff
0000
7fff
ffff8000
=== 8-bit ===
ff
00
7f
ffffff80

Это вызвано спецификатором% x, который ожидает только 32-разрядных целых чисел. Он генерирует неправильный вывод для отрицательных Int8 и Int16.

String(format: '%x', Int16.min)   // outputs ffff8000
String(format: '%x', Int8.min)    // outputs ffffff80

Второй подход заключается в использовании побитовых операторов:

func hex<T: SignedIntegerType>(v: T) -> String {
    var s = ""
    var i = v.toIntMax()
    for _ in 0..<sizeof(T)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

func hex<T: UnsignedIntegerType>(v: T) -> String {
    var s = ""
    var i = v.toUIntMax()
    for _ in 0..<sizeof(T)*2 {
        s = String(format: "%x", i & 0xF) + s
        i = i >> 4
    }
    return s
}

Пока они работают нормально для всех целых чисел, отрицательных и положительных. Вывод тестового кода:

=== 64-bit ===
ffffffffffffffff
0000000000000000
7fffffffffffffff
8000000000000000
=== 32-bit ===
ffffffff
00000000
7fffffff
80000000
=== 16-bit ===
ffff
0000
7fff
8000
=== 8-bit ===
ff
00
7f
80

Ответ 5

Еще одно возможное решение как метод расширения протокола Swift 2, используя константы модификатора ширины печати от <inttypes.h>:

extension IntegerType where Self: CVarArgType {
    var hex : String {
        let format : String
        switch (sizeofValue(self)) {
        case 1:
            format = "%02" + __PRI_8_LENGTH_MODIFIER__ + "X"
        case 2:
            format = "%04" + PRIX16
        case 4:
            format = "%08" + PRIX32
        case 8:
            format = "%016" + __PRI_64_LENGTH_MODIFIER__ + "X"
        default:
            fatalError("Unexpected integer size")
        }
        return String(format: format, self)
    }
}

Это работает правильно для полного диапазона всех подписанных и неподписанных целые типы:

UInt8.max.hex // FF
Int8.max.hex  // 7F
Int8.min.hex  // 80

UInt16.max.hex // FFFF
Int16.max.hex  // 7FFF
Int16.min.hex  // 8000

UInt32.max.hex // FFFFFFFF
Int32.max.hex  // 7FFFFFFF
Int32.min.hex  // 80000000

UInt64.max.hex // FFFFFFFFFFFFFFFF
Int64.max.hex  // 7FFFFFFFFFFFFFFF
Int64.min.hex  // 8000000000000000

Обновление для Swift 3:

extension Integer where Self: CVarArg {
    var hex : String {
        let format : String
        switch MemoryLayout.size(ofValue: self) {
        case 1:
            format = "%02" + __PRI_8_LENGTH_MODIFIER__ + "X"
        case 2:
            format = "%04" + PRIX16
        case 4:
            format = "%08" + PRIX32
        case 8:
            format = "%016" + __PRI_64_LENGTH_MODIFIER__ + "X"
        default:
            fatalError("Unexpected integer size")
        }
        return String(format: format, self)
    }
}