How to convert a double into a byte array in swift?
Solution 1
typealias Byte = UInt8
func toByteArray<T>(var value: T) -> [Byte] {
return withUnsafePointer(&value) {
Array(UnsafeBufferPointer(start: UnsafePointer<Byte>($0), count: sizeof(T)))
}
}
toByteArray(1729.1729)
toByteArray(1729.1729 as Float)
toByteArray(1729)
toByteArray(-1729)
But the results are reversed from your expectations (because of endianness):
[234, 149, 178, 12, 177, 4, 155, 64]
[136, 37, 216, 68]
[193, 6, 0, 0, 0, 0, 0, 0]
[63, 249, 255, 255, 255, 255, 255, 255]
Added:
func fromByteArray<T>(value: [Byte], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
return UnsafePointer<T>($0.baseAddress).memory
}
}
let a: Double = 1729.1729
let b = toByteArray(a) // -> [234, 149, 178, 12, 177, 4, 155, 64]
let c = fromByteArray(b, Double.self) // -> 1729.1729
For Xcode8/Swift3.0:
func toByteArray<T>(_ value: T) -> [UInt8] {
var value = value
return withUnsafePointer(to: &value) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<T>.size) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<T>.size))
}
}
}
func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: T.self, capacity: 1) {
$0.pointee
}
}
}
For Xcode8.1/Swift3.0.1
func toByteArray<T>(_ value: T) -> [UInt8] {
var value = value
return withUnsafeBytes(of: &value) { Array($0) }
}
func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBytes {
$0.baseAddress!.load(as: T.self)
}
}
Solution 2
Well, it wasn't easy, but here it is:
func doubleToByteArray(value: Double) -> [UInt8] {
let count = sizeof(Double)
var doubles: [Double] = [value]
let data = NSData(bytes: doubles, length: count)
var result = [UInt8](count: count, repeatedValue: 0)
data.getBytes(&result, length: count)
return result
}
Use with caution.
Solution 3
Solution in swift 3:
public func toByteArray<T>(_ value: T) -> [Byte] {
let totalBytes = MemoryLayout<T>.size
var value = value
return withUnsafePointer(to: &value) { valuePtr in
return valuePtr.withMemoryRebound(to: Byte.self, capacity: totalBytes) { reboundPtr in
return Array(UnsafeBufferPointer(start: reboundPtr, count: totalBytes))
}
}
}
Solution 4
The accepted answers are dangerous because of the fact that the MemoryLayout
provides you the size of the static type T
!
To workaround the problem you should create a custom protocol and ask for Self
in it:
protocol ByteConvertible {}
extension ByteConvertible {
func toBytes() -> [UInt8] {
let capacity = MemoryLayout<Self>.size
var mutableValue = self
return withUnsafePointer(to: &mutableValue) {
return $0.withMemoryRebound(to: UInt8.self, capacity: capacity) {
return Array(UnsafeBufferPointer(start: $0, count: capacity))
}
}
}
}
I mentioned before that the accepted answers are dangerous and here is an example why:
let num = UInt8(42)
MemoryLayout.size(ofValue: num) //=> 1 byte as expected
let any: Any = num
MemoryLayout.size(ofValue: any) //=> 32 bytes which is what will happen in the generic functions from the all the answers
Swift 3.0
Solution 5
Here's my updated version to the original solution.
/// input: array of bytes
/// -> get pointer to byte array (UnsafeBufferPointer<[Byte]>)
/// -> access its base address
/// -> rebind memory to target type T (UnsafeMutablePointer<T>)
/// -> extract and return the value of target type
func binarytotype <T> (_ value: [Byte], _: T.Type) -> T
{
return value.withUnsafeBufferPointer {
$0.baseAddress!
.withMemoryRebound(to: T.self, capacity: 1) {
$0.pointee
}
}
}
/// input type: value of type T
/// -> get pointer to value of T
/// -> rebind memory to the target type, which is a byte array
/// -> create array with a buffer pointer initialized with the source pointer
/// -> return the resulted array
func typetobinary <T> (_ value: T) -> [Byte]
{
var mv : T = value
let s : Int = MemoryLayout<T>.size
return withUnsafePointer(to: &mv) {
$0.withMemoryRebound(to: Byte.self, capacity: s) {
Array(UnsafeBufferPointer(start: $0, count: s))
}
}
}
PS: Don't forget to replace Byte with UInt8.
Related videos on Youtube
Panini Raman
Updated on July 09, 2022Comments
-
Panini Raman almost 2 years
I know how to do it in java (see here), but I couldn't find a swift equivalent for java's ByteBuffer, and consequently its .putDouble(double value) method.
Basically, I'm looking for a function like this:func doubleToByteArray(value: Double) -> [UInt8]? { . . . } doubleToByteArray(1729.1729) // should return [64, 155, 4, 177, 12, 178, 149, 234]
-
David Gomez over 9 yearsJust use
toByteArray(1729.1729).reverse()
to get the order you are looking for. -
rintaro over 9 yearsI recommend to use functions defined in
CFByteOrder.h
, for exampletoByteArray(CFConvertDoubleHostToSwapped(1729.1729).v)
. -
Michael Dorner over 8 yearsI have added
typealias Byte = UInt8
, becauseByte
isn`t a native data type. :-) -
j.s.com almost 8 yearsI would like to use this in Swift 3 beta 6 and the new withMemoryRebound command. Does anybody know how to convert it to swift 3 beta 6?
-
RenniePet over 7 yearsA word of caution if you're using the first function to serialize data going to some non-Swift platform. If you pass it an "optional" Swift value type then it adds an extra byte to the output, presumably to indicate if the value is nil or not. So giving it a UInt64? value produces 9 bytes instead of 8. One way to avoid this would be to change the parameter type of "value" from T to T?, and then replace the first statement with "var value = value!".
-
Vyacheslav over 7 yearsIt crashes: stackoverflow.com/questions/42252124/…
-
Damiaan Dufaux almost 7 yearsI think
withUnsafeBytes(of: &y, Array.init)
is a more efficient way of converting an UnsafeMutableBufferPointer to an Array. Because the Array initialiser is passed directly.