Как работать с 10-байтовыми данными в gcc?

Модератор: Модераторы разделов

MiK13
Сообщения: 644
ОС: Linux Debian

Как работать с 10-байтовыми данными в gcc?

Сообщение MiK13 » 02.03.2018 00:04

Возникла задача обрабатывать данные с GPS приёмника. С которого идёт последовательность байт.
С разборкой формата NMEA проблем нет -- там обычные текстовые строки, начинающиеся с '$'.
Но приёмник выдаёт данные в формате BINR, а там запись имеет следующую структуру:
0x10, <тип> <данные ... > 0x10, 0x03
Если в данных встречается байт 0x10, то он удваивается. Поэтому я решил просто, найдя заголовок, записывать данные в буфер, а после обнаружения конца записи его разбирать.
И тут возникла проблема. В Документе описана структура данных. Основная информация берётся из записи с типом 0x88 -- вектор состояния. Эта запись имеет длину 69 байтов и содержит значения типов: FP64, FP32, INT16S, INT8U и FP80. Аналогом первых четырёх типов в языке C являются double, float, а также (через ыевштеюр) int16_t и uint8_t.
А вот с FP80 я не могу найти аналога. В языке Pascal это extended. По-моему, в виндовых компиляторах long double занимал 10 байтов. Но в gcc это 12 или даже 16 байтов. Хотя значащих только 10 (в x86).
Пока я это это сделал следующим способом:

Код: Выделить всё

  long double week_time=0;       // чтобы все байты переменой были нулевыми
  memcpy(&week_time,buf+28,10);  // с 28 до 38 байта находится время с начала недели, мс
Но, может быть, есть какие-то более "стандартные" средства?

P.S. Когда я анализировал принимаемые данные (было запрошено передавать их через 5 секунд), значение week_time менялось с шагом 5000. Причём, целая часто всегда была кратна 1000. Но вот дробная часть не была нулевой.

P.P.S. На счёт кратности 1000 я ошибся.
Сейчас проанализировал ещё один массив данных (почти за 11 минут, с шагом 1 секунда).
начальное значение времени было 399910999.753134 мс, конечное -- 399910999.753134 мс
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 14301
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение Bizdelnick » 02.03.2018 01:11

Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
команда
новичок
нюанс
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение NickLion » 02.03.2018 14:12

Bizdelnick
sizeof(__float80) всё равно 16.
Спасибо сказали:

MiK13
Сообщения: 644
ОС: Linux Debian

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение MiK13 » 02.03.2018 22:09


Спасибо за ссылку. Благодаря ей я узнал о типе __float128, когда значащими являются все 16 байтов. Правда, возникла проблема с выводом значений таких переменных. Нашёл, что их надо выводить как float, с модицикатором Q. Но оказалось, что компилятор про этот модификатор не знает и ворчит, что "unknown conversion type character ‘Q’" и "too many arguments for format"
И ещё оказалось, что надо собирать программу с ключом -lquadmath.

Но вот с __float80 это не прошло :-( Похоже, что это обычный 10-байтовый long double, под который отводится 12 (в i386) или 16 (в amd64) байтов. Поэтому пока придётся копировать в него 10 байтов из структуры.
Правда, man gcc выдал, что у компилятора gcc столько опций..., причём многие их них машинно-зависимые. Может быть и можно указать, чтобы он отводил для вещественной переменной только 10 байтов....
Спасибо сказали:

yoshakar
Сообщения: 259
ОС: Debian Stretch

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение yoshakar » 03.03.2018 10:50

MiK13 писал(а):
02.03.2018 22:09
Может быть и можно указать, чтобы он отводил для вещественной переменной только 10 байтов....
Судя по этому ответу, нет: How to define a 80-bit size variable
Спасибо сказали:

Аватара пользователя
s.xbatob
Сообщения: 549
ОС: RfRemix

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение s.xbatob » 03.03.2018 11:08

Вы пошли совсем не туда. Тут очевидный путь — для каждого типа фрейма описать соответствующую структуру, сделать cast-преобразование загруженного фрейма в указатель на нужную структуру и уже с этой структурой комфортно работать. Единственное, что может понадобиться — менять порядок байт в полях если он отличается от "родного"
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 14301
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение Bizdelnick » 03.03.2018 11:22

s.xbatob писал(а):
03.03.2018 11:08
Вы пошли совсем не туда. Тут очевидный путь — для каждого типа фрейма описать соответствующую структуру, сделать cast-преобразование загруженного фрейма в указатель на нужную структуру и уже с этой структурой комфортно работать. Единственное, что может понадобиться — менять порядок байт в полях если он отличается от "родного"

Сначала нужно экранирование убрать, как минимум.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
команда
новичок
нюанс
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение NickLion » 03.03.2018 11:39

s.xbatob писал(а):
03.03.2018 11:08
Вы пошли совсем не туда. Тут очевидный путь — для каждого типа фрейма описать соответствующую структуру, сделать cast-преобразование загруженного фрейма в указатель на нужную структуру и уже с этой структурой комфортно работать. Единственное, что может понадобиться — менять порядок байт в полях если он отличается от "родного"

Я тоже хотел сказать про union, но всё зависит от структуры. Если FP80 идёт не последним, то просто cast или union использовать не получится. Например, структура выглядит так:
FP64 f1
FP80 f2
FP32 f3
FP80 f4
FP32 f5
Удобную структуру не сделать, придётся копировать частями.
Спасибо сказали:

MiK13
Сообщения: 644
ОС: Linux Debian

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение MiK13 » 04.03.2018 17:29

yoshakar писал(а):
03.03.2018 10:50
MiK13 писал(а):
02.03.2018 22:09
Может быть и можно указать, чтобы он отводил для вещественной переменной только 10 байтов....
Судя по этому ответу, нет: How to define a 80-bit size variable

Почитал. Нашёл интересные моменты, связанные с опциями -mlong-double-80 и -mpc80. Но их использование не помогло. Всё равно sizeof(long double) равен 16
Интересный момент в man gcc
-mlong-double-64
-mlong-double-80
-mlong-double-128
These switches control the size of "long double" type. A size of 64 bits makes the
"long double" type equivalent to the "double" type. This is the default for 32-bit
Bionic C library. A size of 128 bits makes the "long double" type equivalent to
the "__float128" type. This is the default for 64-bit Bionic C library.


ТРИ опции -mlong-double- находятся рядом. Но в их описании -mlong-double-80 НЕ УПОМИНАЕТСЯ. Кстати, -mlong-double-64 сработала -- long double стал размером 8 байтов. Но __float80 остался 16.
Что мне больше всего не нравится, так это непонятно, как обеспечить совместимость. Даже в рамках программы с одним исходным кодом.
Если я в рамках программы в системе i386 записал в файл (или передал по сети) запись, в которой есть long double v[N], или даже есть элемент структуры long double, то такая же программа на amd64 эти данные не сможет понять. А в данном случае мне нужно обрабатывать следующую структуру (байтов заголовка и типа, а также завершающих байтов в ней нет(:

Код: Выделить всё

struct Rec88h {
  FP64 lat,lon;            // Широта и долгота, рад
  FP64 h;                  // Высота, м
  FP32 sko;                // СКО плоских координат, м
  FP80 tw;                 // Время определения с начала недели, мс
  INT16S n_w;              // Номер недели по GPS
  FP64 v_lat,v_lon,v_h;    // Скорость по широте, долготе и высоте
  FP32 dev_opg;            // Отклонение периода опорного генератора, мс
  INT8U_t stat;            // Статус решения
} *p88 = (struct Rec88h *) b;
Если с типами FP32, FP64, INT16S и INT8U проблем не возникло, то для FP80 пришлось использовать char tw[10] и потом выполнять
long double tw=0;
memcpy(&tw,p88->tw,10);
Странно то, что в GCC не предусмотрели такую возможность (или, может быть всё-таки предусмотрели?). Но если бы я использовал Free Pascal, то такой проблемы бы не возникло: тип extended имеет длину 10 байтов.
Спасибо сказали:

NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение NickLion » 05.03.2018 12:32

MiK13 писал(а):
04.03.2018 17:29
Что мне больше всего не нравится, так это непонятно, как обеспечить совместимость. Даже в рамках программы с одним исходным кодом.
Если я в рамках программы в системе i386 записал в файл (или передал по сети) запись, в которой есть long double v[N], или даже есть элемент структуры long double, то такая же программа на amd64 эти данные не сможет понять.

Это следствие того, что программа на C (да и C++) может собираться на очень разных платформах. Поэтому особых ограничений на размеры типов нет. Да и на порядок следования байтов. Так что, записанный short int на разных платформах может быть разным. Про float я уже молчу. О страшном long double лучше не вспоминать. Наиболее кроссплатформенным методом будет сериализация в unsigned char[n] и десериализация из него.

MiK13 писал(а):
04.03.2018 17:29
Странно то, что в GCC не предусмотрели такую возможность (или, может быть всё-таки предусмотрели?). Но если бы я использовал Free Pascal, то такой проблемы бы не возникло: тип extended имеет длину 10 байтов.

FreePascal не настолько кросс-платформенный, поэтому имеет более привязанные типы данных.
Спасибо сказали:

MiK13
Сообщения: 644
ОС: Linux Debian

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение MiK13 » 16.03.2018 15:00

NickLion писал(а):
05.03.2018 12:32
FreePascal не настолько кросс-платформенный, поэтому имеет более привязанные типы данных.
Это понятно. Но тут есть маленькое противоречие.
extended на паскале имеет размер 10 байтов как на i386, так и на amd64. А вот long double на i386 имеет размер 12 байтов, а на amd64 -- 16 байтов.
Несколько лет назад я решил сравнить long double у себя, на amd64 и на Солярисе. И там и там это было 16 байтов. Но у меня значащих оказались только 10 байтов, а на солярисе -- все 16. И, по-моему, у них порядок байтов был разный.

Хотелось бы сравнить long double на Solaris и __flooat128 на amd64. Только пока не знаю, где SUN найти.

P.S. Недавно ради интереса купил Raspberry Pi. Если найду время его запустить, посмотрю, что будет там.
Спасибо сказали:

Аватара пользователя
s.xbatob
Сообщения: 549
ОС: RfRemix

Re: Как работать с 10-байтовыми данными в gcc?

Сообщение s.xbatob » 16.03.2018 15:21

MiK13 писал(а):
16.03.2018 15:00
NickLion писал(а):
05.03.2018 12:32
FreePascal не настолько кросс-платформенный, поэтому имеет более привязанные типы данных.
Это понятно. Но тут есть маленькое противоречие.
extended на паскале имеет размер 10 байтов как на i386, так и на amd64. А вот long double на i386 имеет размер 12 байтов, а на amd64 -- 16 байтов.
Несколько лет назад я решил сравнить long double у себя, на amd64 и на Солярисе. И там и там это было 16 байтов. Но у меня значащих оказались только 10 байтов, а на солярисе -- все 16. И, по-моему, у них порядок байтов был разный.

Хотелось бы сравнить long double на Solaris и __flooat128 на amd64. Только пока не знаю, где SUN найти.

P.S. Недавно ради интереса купил Raspberry Pi. Если найду время его запустить, посмотрю, что будет там.

Более того, общепринятый формат есть только у четырёхбайтовых float. Естественно, с точностью до порядка байт. У всего, что длиннее, ещё и размеры мантиссы и характеристики могут различаться. А у Intel&Co вблизи overflow/underflow ещё какие-то особые случаи предусмотрены.
Связываться с вещественными числами вообще последнее дело, а уж по сети -- тем более.
Спасибо сказали:

Вернуться в «Программирование для начинающих»