LINUX.ORG.RU

Сохранение/загрузка вещественных чисел в файл

 


1

7

Допустим, перед нами стоит задача сохранения данных в файл для последующей загрузки. В целях экономии места и повышения производительности файл хочется сделать бинарным (формат хранимых структур очень простой и точно никогда не изменится). При этом, однако, хочется кросс-платформенности (чтобы файл сохранённый, скажем, на x86, нормально прочитался программой, собранной под ARM и т. д.).

С целыми числами всё понятно - используем типы фиксированной разрядности (типа int32_t), также при сохранении переводим к фиксированному порядку байт (либо всегда в little-endian, либо всегда в big-endian) как это делает htonl и аналоги. По идее этого должно быть достаточно.

А что насчёт float/double? Насколько их внутреннее представление может отличаться между платформами, поддерживающими стандарт Си? Как можно максимально эффективно преобразовать их в единую форму для сохранения и обратно? (может быть есть какие-то стандартные функции или хотя бы хорошие библиотеки, либо вовсе достаточно 2-3 строчек без всяких библиотек).

★★★★★

Исходники Java открыты. Посмотри там.

iZEN ★★★★★ ()

Может, лучше текстом? JSON, XML.

точно никогда не изменится

146% :)

anonymous ()
Ответ на: комментарий от Crocodoom

может быть есть какие-то стандартные функции

fwrite, fread?

Crocodoom ★★★★★ ()

В целях экономии места и повышения производительности файл хочется сделать бинарным

Первое неверное допущение. Дальше можно не читать. Хочешь переносимости, храни в тексте.

beastie ★★★★★ ()
Annex F
(normative)

[...]

An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex. 356)

[...]

356) Implementations that do not define __STDC_IEC_559__ are not required to conform to these specifications.

И учитывая, что именно IEEE754 (он же IEC559) расписывает бинарный формат, стандарт гарантий не даёт. Наиболее переносимо будет текстом скорее всего, но если на всех целевых системах поддерживается IEEE754, то можно типы писать как есть (учитывая big/little endian). И long double не использовать, он разный может быть.

xaizek ★★★★★ ()
Последнее исправление: xaizek (всего исправлений: 1)

ЕМНИП, для floating point не стандартизирован endianess, хоть большинство архитектур/платформ использует тот же порядок, что и для целых. Я бы в тексте сохранял, в общем

Deleted ()

Если

С целыми числами всё понятно

то почему бы не хранить отдельно целую часть, отдельно дробную, как целые?

Либо отдельно мантисса, отдельно степень, для экспоненциальной записи?

vvn_black ★★★★★ ()

Для x86/arm не критично, но желательно иметь код софтового преобразования в 754 LE и активировать его на неизвестрых платформах и на юниттестах

mittorn ★★★★★ ()
Ответ на: комментарий от beastie

В целях экономии места и повышения производительности файл хочется сделать бинарным


Первое неверное допущение.

Серьезно?? Получается, что бинарный формат не подходит под «экономия места» и «повышение производительности?

ТСу: текстовый формат действительно самый беспроигрышный, но надо внимательно смотреть, чтобы сохранилось нужное число знаков. Иначе всякие отличия в один ulps и прочая жесть. Место сэкономить можно компрессором (тот же lz4).

Если никак без бинарного формата, попробуй посмотреть, как NPY устроен, точнее кусок про floating point. Там заявлена кроссплатформа.

lu4nik ★★★ ()
Последнее исправление: lu4nik (всего исправлений: 1)
Ответ на: комментарий от lu4nik

Серьезно??

В общем случае это CPU/RAM tread-off. Экономим байты, теряем производительность.

В данном же конкретном случае — классическая pre-mature optimisation.

Пусть сначала ответит на ворпос, является ли это узким местом и оправдывает ли решение использовать бинарый формат добавленную алгоретмическую сложность.

beastie ★★★★★ ()
Ответ на: комментарий от beastie

В общем случае это CPU/RAM tread-off. Экономим байты, теряем производительность.

На чём теряем производительность то? По-твоему читать ASCII флоты быстрее, чем бинарные? Или писать?

Crocodoom ★★★★★ ()

Посмотри, как в FITS-файлах сохраняются изображения и таблицы. Там, несмотря на то, что данные бинарные, файл нормально открывается и на тупоконечных, и на остроконечных машинах.

anonymous ()
Ответ на: комментарий от anonymous

Кстати, да: тоже очень хороший вариант.

anonymous ()
Ответ на: комментарий от anonymous

Да можно и без memcpy: просто union сделать, и все!

anonymous ()
Ответ на: комментарий от anonymous

Вот готовая функция, вдохновлённая этим комментом:

void serialize_double(uint8_t buf[sizeof(double)], double x) {
    uint64_t x_int;
    memcpy(&x_int, &x, sizeof(x));
    x_int = htobe64(x_int);
    memcpy(buf, &x_int, sizeof(x));
}

anonymous ()
Ответ на: комментарий от beastie

Алгоритмическая сложность перевода в текст и обратно как раз выше, чем сложность простой записи бинарных данных.

anonymous ()
Ответ на: комментарий от anonymous

Это никакого отношения к реальности не имеет — любой компьютер мегабайтный текстовый файл обработает достаточно быстро. Без разницы: сериализованные там данные, или бинарные.

Другое дело — объем файла! И т.к. числа с плавающей точкой компактней всего писать в бинарном виде, все и продолжают писать в бинарном, не заморачиваясь с голимой сериализацией!

anonymous ()
Ответ на: комментарий от anonymous

Это если __STDC_IEC_559__. А если нет, то как преобразовать double в этот формат?

KivApple ★★★★★ ()
Ответ на: комментарий от xaizek

но если на всех целевых системах поддерживается IEEE754,

а теперь сходу назовите современные платформы, которые не поддерживают IEEE754 :)

Harald ★★★★★ ()

https://en.wikipedia.org/wiki/Endianness#Floating-point

Although the ubiquitous x86 processors of today use little-endian storage for all types of data (integer, floating point, BCD), there are a number of hardware architectures where floating-point numbers are represented in big-endian form while integers are represented in little-endian form.[17] There are ARM processors that have half little-endian, half big-endian floating-point representation for double-precision numbers: both 32-bit words are stored in little-endian like integer registers, but the most significant one first. Because there have been many floating-point formats with no «network» standard representation for them, the XDR standard uses big-endian IEEE 754 as its representation. It may therefore appear strange that the widespread IEEE 754 floating-point standard does not specify endianness.[18] Theoretically, this means that even standard IEEE floating-point data written by one machine might not be readable by another. However, on modern standard computers (i.e., implementing IEEE 754), one may in practice safely assume that the endianness is the same for floating-point numbers as for integers, making the conversion straightforward regardless of data type. (Small embedded systems using special floating-point formats may be another matter however.)

Еще в glibc есть какие-то union-ы для float и double https://code.woboq.org/userspace/glibc/sysdeps/ieee754/ldbl-128/ieee754.h.html

Можно договориться о порядке мантиссы, экспоненты и положения знакового бита, и о endian для самой мантиссы и экспоненты, и там уже в ifdef-ы это как-нибудь завернуть для единообразия

SZT ★★★★★ ()
Последнее исправление: SZT (всего исправлений: 1)

а мне вот сама постановка задачи ...

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

FITS уже советовали, наверное есть другие варианты, но мы их как-то даже не с самого начала начали рассматривать.

Мистика...

sshestov ()

Сохраняйте в msgpack.

Хорошая и до недавнего времени header-only библиотека - msgpuck.

Sorcerer ★★★★★ ()
Ответ на: комментарий от KivApple

Тупоконечных микроконтроллеров кот наплакал. Среднеконечных вообще хрен найдешь. В общем, с МК все намного проще...

anonymous ()

IEEE 754, отличается только порядок байт

anonymous ()
Ответ на: комментарий от beastie

Профнепригодная дурашка. Текст в байты тоже можно закодировать разными несовместимыми способами, а полагать что «везде ascii» такое же заблуждение как и полагать что «везде little endian». Коли уж ты готов ограничить совместимость двоичным потоком 8-битовых байт (что будет несовместимо с троичными машинами и другим количеством бит в байте), то у текста никакого преимущества нет, зато раздутость и медленный парсинг.

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

что будет несовместимо с троичными машинами и другим количеством бит в байте

Ты из какого века к нам приехал? Из 30-го???

Где ты в реальной жизни видел такое, чтобы младшая таблица кодировки с ASCII не совпадала? Это кто ж такое сделает — полный ССЗБ???

anonymous ()
Ответ на: комментарий от t184256

Вы меня с кем-то перепутали. Я как раз за сохранение в бинарный формат, посколько это и быстрее работает, и занимает меньше памяти.

Crocodoom ★★★★★ ()

По поводу перевода из одного порядка байт в другой. Когда мне было такое нужно, я использовал каст float* к uint32_t*, после чего __builtin_bswap32 по каждому элементу массива. Правда вопрос переносимости у меня тогда не стоял, наверное это будет работать только на GCC.

при сохранении переводим к фиксированному порядку байт (либо всегда в little-endian, либо всегда в big-endian)

Много лишних конвертаций будет. Я бы лучше добавил флаг endianness в свой формат.

Crocodoom ★★★★★ ()
Ответ на: комментарий от Crocodoom

Вот я и удивляюсь, что ты его только по языку поправил.

t184256 ★★★★★ ()
Ответ на: комментарий от beastie

Пока вы её на мне поддерживаете своей неспособностью возразить по существу - нет, не жмёт.

slovazap ★★★★★ ()

формат хранимых структур очень простой и точно никогда не изменится

О, сколько раз возникала подобная надежда...

Но вообще, посмотри в сторону Google Protobuf. Не исключено, что для конкретной задачи это забивание гвоздей микроскопом, но вообще, он именно для этого и создавался: совместимость, переносимость, двоичные структуры высокой сложности при сохранении высокой эффективности.

hobbit ★★★★★ ()

asn1 как способ организации хранения не предлагали?

Silerus ★★★ ()
Ответ на: комментарий от slovazap

двоичным потоком 8-битовых байт (что будет несовместимо с троичными машинами и другим количеством бит в байте)

Интересно... Я полагал, что, например, библиотека ввода-вывода скроет эти нюансы. Иначе они(машины) что-либо записать смогут только в собственных кодировках(кодировки ведь на размер символа завязаны). Можно подробнее?

Deleted ()

Добавь в формат хедер с номером версии формата, флагом порядка байт и представления флоатов. Данные сохраняй как есть с помощью mmap. Вероятность, что на практике попадется бигендиан или не 754 машина невелика.

anonymous ()

Скопипасть из protobuf как они делают.

vzzo ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.