В настоящее время я работаю над конвертером файлов. Я никогда раньше ничего не делал, используя чтение бинарных файлов. Для этого типа файлов доступно много конвертеров (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.
Если кто-нибудь может помочь мне с английским объяснением того, что означает спецификация, или любыми указаниями о том, что делать, я был бы очень признателен.
Спасибо!
Согласно документу, этот код возвращает 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-байтовом Настоящий. Я не уверен, что это может быть проблемой или нет.