I want to convert an NSDecimalNumber to a byte array. Also, I do not want to use any library for that like BigInt, BInt, etc.
I tried this one:
static func toByteArray<T>(_ value: T) -> [UInt8] {
var value = value
return withUnsafeBytes(of: &value) { Array($0) }
}
let value = NSDecimalNumber(string: "...")
let bytes = toByteArray(value.uint64Value)
If the number is not bigger than Uint64, it works great. But, what if it is, how can we convert it?
The problem is obviously the use of uint64Value
, which obviously cannot represent any value greater than UInt64.max
, and your example, 59,785,897,542,892,656,787,456, is larger than that.
If you want to grab the byte representations of the 128 bit mantissa, you can use _mantissa
tuple of UInt16
words of Decimal
, and convert them to bytes if you want. E.g.
extension Decimal {
var byteArray: [UInt8] {
return [_mantissa.0,
_mantissa.1,
_mantissa.2,
_mantissa.3,
_mantissa.4,
_mantissa.5,
_mantissa.6,
_mantissa.7]
.flatMap { [UInt8($0 & 0xff), UInt8($0 >> 8)] }
}
}
And
if let foo = Decimal(string: "59785897542892656787456") {
print(foo.byteArray)
}
Returning:
[0, 0, 0, 0, 0, 0, 0, 0, 169, 12, 0, 0, 0, 0, 0, 0]
This, admittedly, only defers the problem, breaking the 64-bit limit of your uint64Value
approach, but is still constrained to the inherent 128-bit limit of NSDecimalNumber
/Decimal
. To capture numbers greater than 128 bits, you'd need a completely different representation.
NB: This also assumes that the exponent is 0
. If, however, you had some large number, e.g. 4.2e101 (4.2 * 10101), the exponent will be 100
and the mantissa will simply be 42
, which I bet is probably not what you want in your byte array. Then again, this is an example of a number that is too large to represent as a single 128 bit integer, anyway:
if let foo = Decimal(string: "4.2e101") {
print(foo.byteArray)
print(foo.exponent)
}
Yielding:
[42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
100