Swift: Как конвертировать байты в число с плавающей запятой / получить более точное число? - PullRequest
0 голосов
/ 06 апреля 2019

Я получаю данные акселерометра от устройства BLE в виде 6 байтов и хочу преобразовать их в значение с плавающей запятой, чтобы быть как можно более точным.

Каждые 2 байта представляют разные оси (x, y и z соответственно). Вместо числа, например 982, я хочу число, например, 0,98243873

.

Я пытался преобразовать байты в число с плавающей точкой, пытаясь ...

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

но я получаю странные числа, такие как -6.777109e-21, когда значение Int16 равно 1047, и я ожидаю что-то вроде 1.04793283. Будет ли это как-то связано с подписью байтов? Могу ли я получить такую ​​точность из двух байтов?

Ответы [ 2 ]

0 голосов
/ 07 апреля 2019

Проблема в том, что вы пытаетесь преобразовать значения UInt32 с прямым порядком байтов в Float просто путем "переинтерпретации" тех же битовых комбинаций, что и новое значение (для этого Float(bitPattern:)), но это не так все как Float хранит свои данные. Типы данных Swift Float и Double являются реализациями 32- и 64-битных типов данных с плавающей запятой из IEEE 754 . Есть много онлайн-ресурсов, которые объясняют это, но 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)
0 голосов
/ 06 апреля 2019

Если вы получите 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;
...