Как преобразовать 8-байтный шестнадцатеричный код в реальный

В настоящее время я работаю над конвертером файлов. Я никогда раньше ничего не делал, используя чтение бинарных файлов. Для этого типа файлов доступно много конвертеров (gdsII в текст), но я не могу найти ни одного в Swift.

У меня работают все остальные типы данных (2 байта int, 4 байта int), но я действительно борюсь с реальным типом данных.

Из спецдокумента: http://www.cnf.cornell.edu/cnf_spie9.html

Вещественные числа не представлены в формате IEEE. Число с плавающей запятой состоит из трех частей: знака, экспоненты и мантиссы. Значение числа определяется как (мантисса) (16) (показатель степени). Если «S» — бит знака, «E» — биты экспоненты, а «M» — биты мантиссы, то 8-байтовое действительное число имеет формат

SEEEEEEE MMMMMMMM MMMMMMMM MMMMMMMM
MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM

Показатель степени указан в обозначении «превышение 64»; то есть 7-битное поле показывает число, которое на 64 больше фактического показателя степени. Мантисса всегда представляет собой положительную дробь, большую или равную 1/16 и меньшую 1. Для 8-байтового действительного числа мантисса находится в битах с 8 по 63. Десятичная точка двоичной мантиссы находится слева от бита. 8. Бит 8 представляет значение 1/2, бит 9 представляет 1/4 и так далее.

Я пытался реализовать что-то похожее на то, что я видел в python или Perl, но у каждого языка есть функции, которых нет у Swift, а также преобразование типов становится очень запутанным.

Это один из испробованных мной методов, основанный на Perl. Не похоже, чтобы получить правильное значение. Побитовая математика для меня нова.

       var sgn = 1.0
        let andSgn = 0x8000000000000000 & bytes8_test
        if ( andSgn > 0) { sgn = -1.0 }

       // var sgn = -1 if 0x8000000000000000 & num else 1
        let manta = bytes8_test & 0x00ffffffffffffff
        let exp = (bytes8_test >> 56) & 0x7f
        let powBase = sgn * Double(manta)
        let expPow = (4.0 * (Double(exp) - 64.0) - 56.0)
        var testReal = pow( powBase , expPow )

Еще я пробовал:

let bitArrayDecode = decodeBitArray(bitArray: bitArray)
        let valueArray = calcValueOfArray(bitArray: bitArrayDecode)
        var exponent:Int16
        //calculate exponent
        if (negative){
            exponent = valueArray - 192
        } else {
            exponent = valueArray - 64
        }
        //calculate mantessa
        var mantissa = 0.0
        //sgn = -1 if 0x8000000000000000 & num else 1
        //mant = num & 0x00ffffffffffffff
        //exp = (num >> 56) & 0x7f
        //return math.ldexp(sgn * mant, 4 * (exp - 64) - 56)

        for index in 0...7 {
            //let mantaByte = bytes8_1st[index]
            //mantissa +=  Double(mantaByte) / pow(256.0, Double(index))
            let bit = pow(2.0, Double(7-index))
            let scaleBit = pow(2.0, Double( index ))
            var mantab = (8.0 * Double( bytes8_1st[1] & UInt8(bit)))/(bit*scaleBit)
            mantissa = mantissa + mantab
            mantab = (8.0 * Double( bytes8_1st[2] & UInt8(bit)))/(256.0 * bit * scaleBit)
            mantissa = mantissa + mantab
            mantab = (8.0 * Double( bytes8_1st[3] & UInt8(bit)))/(256.0 * bit * scaleBit)
            mantissa = mantissa + mantab

        }
        let real = mantissa * pow(16.0, Double(exponent))

ОБНОВИТЬ:

Следующая часть, кажется, работает для экспоненты. Возвращает -9 для набора данных, с которым я работаю. Чего я и ожидаю.

        var exp = Int16((bytes8 >> 56) & 0x7f)
        exp = exp - 65 //change from excess 64
        print(exp)

        var sgnVal = 0x8000000000000000 & bytes8
        var sgn = 1.0
        if (sgnVal == 1){
            sgn = -1.0
        }

Для мантиссы, хотя я не могу как-то правильно рассчитать.

Набор данных: 3d 68 дб 8b ас 71 0c b4 38 6d f3 7f 67 5e f6 эк

Я думаю, что он должен возвращать 1e-9 для показателя степени и 0,0001.

Ближе всего я получил настоящий Double 0.0000000000034907316148746757

     var bytes7 = Array<UInt8>()
        for (index, by) in data.enumerated(){
            if (index < 4) {
             bytes7.append(by[0])
             bytes7.append(by[1])
            }
        }
        for index in 0...7 {
            mantissa += Double(bytes7[index]) / (pow(256.0, Double(index) + 1.0 ))
        }
        var real =  mantissa * pow(16.0, Double(exp));
        print(mantissa)

КОНЕЦ ОБНОВЛЕНИЯ.

Также, похоже, не дает правильных значений. Этот был основан на файле C.

Если кто-нибудь может помочь мне с английским объяснением того, что означает спецификация, или любыми указаниями о том, что делать, я был бы очень признателен.

Спасибо!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
657
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Согласно документу, этот код возвращает 8-байтовые данные Настоящий как Double.

extension Data {
    func readUInt64BE(_ offset: Int) -> UInt64 {
        var value: UInt64 = 0
        _ = Swift.withUnsafeMutableBytes(of: &value) {bytes in
            copyBytes(to: bytes, from: offset..<offset+8)
        }
        return value.bigEndian
    }
    func readReal64(_ offset: Int) -> Double {
        let bitPattern = readUInt64BE(offset)
        let sign: FloatingPointSign = (bitPattern & 0x80000000_00000000) != 0 ? .minus: .plus
        let exponent = (Int((bitPattern >> 56) & 0x00000000_0000007F)-64) * 4 - 56
        let significand = Double(bitPattern & 0x00FFFFFF_FFFFFFFF)
        let result = Double(sign: sign, exponent: exponent, significand: significand)
        return result
    }
}

Применение:

//Two 8-byte Real data taken from the example in the doc
let data = Data([
    //1.0000000000000E-03
    0x3e, 0x41, 0x89, 0x37, 0x4b, 0xc6, 0xa7, 0xef,
    //1.0000000000000E-09
    0x39, 0x44, 0xb8, 0x2f, 0xa0, 0x9b, 0x5a, 0x54,
])
let real1 = data.readReal64(0)
let real2 = data.readReal64(8)
print(real1, real2) //->0.001 1e-09

Другой пример из "ОБНОВЛЕНИЯ":

//0.0001 in "UPDATE"
let data = Data([0x3d, 0x68, 0xdb, 0x8b, 0xac, 0x71, 0x0c, 0xb4, 0x38, 0x6d, 0xf3, 0x7f, 0x67, 0x5e, 0xf6, 0xec])
let real = data.readReal64(0)
print(real) //->0.0001

Помните, что Double имеет только 52-битную мантиссу (мантисса), поэтому этот код теряет некоторые значащие биты в исходном 8-байтовом Настоящий. Я не уверен, что это может быть проблемой или нет.

Другие вопросы по теме