How to convert a double into a byte array in swift?

22,903

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.

Share:
22,903

Related videos on Youtube

Panini Raman
Author by

Panini Raman

Updated on July 09, 2022

Comments

  • Panini Raman
    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
    David Gomez over 9 years
    Just use toByteArray(1729.1729).reverse() to get the order you are looking for.
  • rintaro
    rintaro over 9 years
    I recommend to use functions defined in CFByteOrder.h, for example toByteArray(CFConvertDoubleHostToSwapped(1729.1729).v).
  • Michael Dorner
    Michael Dorner over 8 years
    I have added typealias Byte = UInt8, because Byte isn`t a native data type. :-)
  • j.s.com
    j.s.com almost 8 years
    I 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
    RenniePet over 7 years
    A 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
    Vyacheslav over 7 years
  • Damiaan Dufaux
    Damiaan Dufaux almost 7 years
    I 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.