One possible solution is to use NSDecimalNumber
to hold the large value. But it doesn't have any built in way to convert the number into a string other than base 10.
The following is an extension to NSDecimalNumber
that will convert the number into any base from 2 to 16. And it also includes a convenience init
that takes a string in a given base.
extension NSDecimalNumber {
convenience init(string: String, base: Int) {
guard base >= 2 && base <= 16 else { fatalError("Invalid base") }
let digits = "0123456789ABCDEF"
let baseNum = NSDecimalNumber(value: base)
var res = NSDecimalNumber(value: 0)
for ch in string {
let index = digits.index(of: ch)!
let digit = digits.distance(from: digits.startIndex, to: index)
res = res.multiplying(by: baseNum).adding(NSDecimalNumber(value: digit))
}
self.init(decimal: res.decimalValue)
}
func toBase(_ base: Int) -> String {
guard base >= 2 && base <= 16 else { fatalError("Invalid base") }
// Support higher bases by added more digits
let digits = "0123456789ABCDEF"
let rounding = NSDecimalNumberHandler(roundingMode: .down, scale: 0, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false)
let baseNum = NSDecimalNumber(value: base)
var res = ""
var val = self
while val.compare(0) == .orderedDescending {
let next = val.dividing(by: baseNum, withBehavior: rounding)
let round = next.multiplying(by: baseNum)
let diff = val.subtracting(round)
let digit = diff.intValue
let index = digits.index(digits.startIndex, offsetBy: digit)
res.insert(digits[index], at: res.startIndex)
val = next
}
return res
}
}
Test:
let bigNum = NSDecimalNumber(string: "18446744073709551615")
print(bigNum.toBase(16))
print(bigNum.toBase(10)) // or just print(bigNum)
print(NSDecimalNumber(string: "B7", base: 16))
print(NSDecimalNumber(string: NSDecimalNumber(string: "18446744073709551615").toBase(16), base: 16))
Output:
FFFFFFFFFFFFFFFF
18446744073709551615
183
18446744073709551615