Swift: 如何将字节转换为浮点数/得到更精确的数字?
Swift: How to convert Bytes into a float / get a more precise number?
我正在从 BLE 设备获取 6 字节形式的加速度计数据,我想将其转换为尽可能精确的浮点值。
每 2 个字节代表一个不同的轴(分别为 x、y 和 z)。我想要一个像 0.98243873
这样的数字,而不是像 982 这样的数字
我尝试通过尝试将字节转换为浮点数...
let data = characteristic.value!
let floatX = Float(bitPattern:UInt32(littleEndian:data[0...1].withUnsafeBytes{[=11=].pointee}))
OR
let floatX = data[0...1].withUnsafeBytes{[=11=].pointee} as Float
但是当 Int16 值为 1047
时,我得到了像 -6.777109e-21
这样的奇怪数字,而我期待的是像 1.04793283
这样的数字。它与被签名的字节有关吗?我什至可以从两个字节中获得这样的精度吗?
如果您收到 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;
问题是您只是试图通过 "reinterpreting" 与新值相同的位模式(这就是 Float(bitPattern:)
是为了),但这根本不是 Float 存储数据的方式。 Swift 的 Float
和 Double
数据类型是 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: [=10=]) }
.withUnsafeBufferPointer { Data(buffer: [=10=]) }
}
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([=10=]) / 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)
我正在从 BLE 设备获取 6 字节形式的加速度计数据,我想将其转换为尽可能精确的浮点值。
每 2 个字节代表一个不同的轴(分别为 x、y 和 z)。我想要一个像 0.98243873
这样的数字,而不是像 982 这样的数字我尝试通过尝试将字节转换为浮点数...
let data = characteristic.value!
let floatX = Float(bitPattern:UInt32(littleEndian:data[0...1].withUnsafeBytes{[=11=].pointee}))
OR
let floatX = data[0...1].withUnsafeBytes{[=11=].pointee} as Float
但是当 Int16 值为 1047
时,我得到了像 -6.777109e-21
这样的奇怪数字,而我期待的是像 1.04793283
这样的数字。它与被签名的字节有关吗?我什至可以从两个字节中获得这样的精度吗?
如果您收到 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;
问题是您只是试图通过 "reinterpreting" 与新值相同的位模式(这就是 Float(bitPattern:)
是为了),但这根本不是 Float 存储数据的方式。 Swift 的 Float
和 Double
数据类型是 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: [=10=]) }
.withUnsafeBufferPointer { Data(buffer: [=10=]) }
}
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([=10=]) / 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)