Swift:如何将Bytes转换成float /获得更精确的数字?

时间:2019-04-05 23:53:27

标签: swift math floating-point byte core-bluetooth

我正在从BLE设备获取6字节形式的加速度计数据,我想将其转换为尽可能精确的浮点值。

每2个字节代表一个不同的轴(分别为x,y和z)。我想要的不是0.9982这样的数字,而是像0.98243873这样的数字。

我尝试通过尝试将字节转换为Float ...

let data = characteristic.value!

let floatX = Float(bitPattern:UInt32(littleEndian:data[0...1].withUnsafeBytes{$0.pointee}))

OR

let floatX = data[0...1].withUnsafeBytes{$0.pointee} as Float

但是当Int16值为-6.777109e-21时,我得到了像1047这样的怪异数字,并且我期望像1.04793283这样的东西。它与被签名的字节有关吗?我什至可以从两个字节获得这样的精度吗?

2 个答案:

答案 0 :(得分:0)

如果您收到6个字节,其中每个2个字节代表一个轴,则实际上您会收到3个数字,每个数字16位整数。要将它们转换为所需的浮动类型(1047-> 1.047xxx),显然必须将它们分别除以1000.0

我不了解Swift,但只需执行以下伪代码:

x = (first_byte + 256 * second_byte) / 1000.0;  // or vice versa, if big-endian
y = (third_byte + 256 * fourth_byte) / 1000.0;  // same...
z = (fifth_byte + 256 * sixth_byte) / 1000.0;   // same again

正如我指出的那样,如果从设备出来的字节是大端的,那么您显然可以这样做:

x = (256 * first_byte + second_byte) / 1000.0; 
// etc...

您也许可以通过乘以倒数(即* 0.001)来加快速度,因为乘法通常比除法快一点。但是,如果某些编译器注意到这是一个常量,则会为您执行此操作:

x = (...) * 0.001;

答案 1 :(得分:0)

问题在于,您仅通过“重新解释”与新值相同的位模式来尝试将小端UInt32值转换为Float(这就是Float(bitPattern:)的含义) ),但这根本不是Float存储数据的方式。 Swift的FloatDouble数据类型是IEEE 754中32位和64位浮点数据类型的实现。有很多在线资源可以解释这一点,但TL; DR的存储方式与科学计数法相似,尾数提高到指数的幂。

我认为您的部分困难来自一次尝试做太多事情。将其分解成小块。编写一个函数,将您的数据分解为3个UInt32组件。然后编写一个单独的函数,对这些组件进行所需的任何转换,例如将它们转换为浮点数。这是一个粗略的示例:

import Foundation

func createTestData(x: UInt32, y: UInt32, z: UInt32) -> Data {
    return [x, y, z]
        .map { UInt32(littleEndian: $0) }
        .withUnsafeBufferPointer { Data(buffer: $0) }
}

func decode(data: Data) -> (x: UInt32, y: UInt32, z: UInt32) {
    let values = data.withUnsafeBytes { bufferPointer in
            bufferPointer
                .bindMemory(to: UInt32.self)
                .map { rawBitPattern in
                    return UInt32(littleEndian: rawBitPattern)
                }
        }

    assert(values.count == 3)
    return (x: values[0], y: values[1], z: values[2])
}

func transform(ints: (x: UInt32, y: UInt32, z: UInt32))
    -> (x: Float, y: Float, z: Float) {
    let transform: (UInt32) -> Float = { Float($0) / 1000 } // define whatever transformation you need
    return (transform(ints.x), transform(ints.y), transform(ints.z))
}

let testData = createTestData(x: 123, y: 456, z: 789)
print(testData) // => 12 bytes
let decodedVector = decode(data: testData)
print(decodedVector) // => (x: 123, y: 456, z: 789)
let intsToFloats = transform(ints: decodedVector)
print(intsToFloats) // => (x: 0.123, y: 0.456, z: 0.789)