Как создать общую функцию 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)
}
}