2017-02-08 1 views
1

У меня есть библиотека, которая сохраняет в дисках нагрузки данных с плавающей запятой в текстовой форме. Кажется, они сделали это из-за важности переносимости, но из-за огромного использования диска из этого я написал функцию, чтобы сохранить двоичное представление плавающих точек непосредственно на диск. Я знаю, что это не гарантирует 100% -ную переносимость, но я буду запускать это только на x86 (_64) Linux/Windows PC (возможно, также в Mac и BSD).двоичная переносимость с плавающей запятой

Есть ли способ, по крайней мере, проверить, является ли формат с плавающей запятой программой понятным, также хорошо с системой? И сколько из несовместимости я должен ожидать от обработки данных с плавающей запятой в двоичной форме?

+0

В ограниченном количестве. '__STDC_IEC_559__' расширяется до' 1', если поддерживается IEC 60559. Но если вы собираетесь передавать файл между платформами, вам это не будет очень полезно. Вам все равно нужно использовать агностический формат платформы. – StoryTeller

+0

Сложно сказать, теоретически стандарт C не навязывает двоичное представление чисел, означающее, что любой компилятор или система могут свободно использовать все, что ему нравится. IEEE-754 определяет бинарный формат с плавающей запятой, использование такого кодирования должно по меньшей мере обеспечивать достаточную совместимость, но трудно быть уверенным, что оно поддерживается в любой системе. Но, как точка в пользу, можно написать функцию для преобразования формата. –

ответ

2

Есть ли способ, по крайней мере, проверить, соответствует ли программа с плавающей запятой программа, хорошо ли она с системой?

Тест 1: размерof. Тест 2: сохраните волшебное значение с плавающей запятой в заголовке вашего файла на диске и проверьте, что программа имеет правильное значение после того, как вы прочитали двоичные данные с диска. Это должно быть достаточно безопасным.

И сколько я должен ожидать от несовместимости с данными с плавающей запятой в двоичной форме?

Очень мало. Если, как вы говорите, вы остаетесь с одной аппаратной архитектурой (x86), все будет в порядке. Если у вас ограниченный набор поддерживаемых архитектур - просто проверьте все из них. На x86 все будут использовать аппаратную плавающую точку, которая ограничивает их творческий подход (в значительной степени не совсем). Даже между архитектурами все, кого я знаю о том, кто использует IEEE 754 с плавающей запятой, имеют одно и то же двоичное представление для одной и той же сущности.

Плавающая точка имеет странную проблему: нет широко используемого стандарта для их двоичного кода на диске/на проводном представлении. При этом каждый, кого я смотрел, делает одну из двух вещей: либо строки, либо хранит бит-шаблон в одинаковом размере целого числа, настраивается для суждения, жестоко бросается в float.

+0

Хороший ответ в целом, только один изъян: жестокое приведение к float приведет к неопределенному поведению со строгими правилами псевдонимов, если вы набрасываете указатель на 'int' или аналогичный. Существует всего два способа обойти это: применять только к/из указателя 'char' и копировать байты или использовать' memcpy() 'вместо трансляции. Позже проще и обычно переводит на лучший код. – cmaster

+0

@cmaster Я сказал жестоко, что я, вероятно, имел в виду «преобразовать двоичный рисунок, используя любой удобный вам способ». Я бы бросил. Конечно, стандарт говорит, что мы не должны бросать. Но я обычно сужу о таких вещах, видя, в какой компании я буду участвовать, когда произойдет нестандартный трюк. И в этом случае компания - это почти все. Стандарт имеет очень веские причины, чтобы сделать его неопределенным, поскольку он полностью неспособен. С другой стороны, у компиляторов есть очень веские причины никогда не нарушать его, потому что они ничего не получают от этого, и все будут ненавидеть их за это. – Art

+0

1. В этом случае вы не в моей компании. Когда я знаю что-то UB, я стараюсь избегать этого. Он чувствует себя слишком осторожно, чтобы не делать этого. 2. UB в вопросе возникает из строгих правил псевдонимов: компилятор может свободно перемещать запись в 'int' после загрузки' float'. В этом и заключается смысл введения правил строгой сглаживания: создать больше ситуаций, когда компилятору разрешено перемещать запись/загрузку. ** Если разработчики компилятора не реализуют явные исключения для своих оптимизаторов, чтобы сохранить ваш код от взлома, любое обновление компилятора может сломать его. ** – cmaster

1

Вы можете посмотреть новые (C11) и старые макросы в заголовке <float.h>, page 46: 5.2.4.2.2 Characteristics of floating types.

+0

Другими словами, даже если у вас этого нет (поскольку вы не находитесь на C11), вы можете повторно реализовать ту же функциональность и пометить файл соответствующими значениями, таким образом, описав значение бит и сделав его возможным для совместимости даже с оборудованием с несогласованным форматом. Разумеется, вы также можете сделать заголовок одним четко определенным поплавком и проверить его (я предлагаю π). – unwind

+0

Какой конкретный макрос вы имеете в виду (или вы имели в виду «макросы»?), И что вы подразумеваете под «новым»? 'DBL_EPSILON',' DBL_MAX', 'DBL_MIN' были вокруг с C89. –

+0

@MarkDickinson Это единственное, что я нашел в стандарте о плавающем числе, в этой проблеме нет стандарта, но это начало. "- дополнительные характеристики с плавающей запятой в ", C11. – Stargateur

-1

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

As noted in this question, то, что спецификация не указывает, является точное отображение битов в байты, поэтому существует потенциал для endianness issues (хотя, вероятно, нет, если вы используете исключительно x86/x86_64). Возможно, было бы неплохо включить значение проверки с плавающей запятой в начало вашего потока (обратите внимание, что недостаточно проверить консистенцию ваших целых чисел, так как технически возможно иметь различную ориентацию для целочисленной и с плавающей запятой).

Если вы пишете текст, одна из возможных альтернатив - hex float format, которая может быть намного быстрее для чтения/записи, чем десятичные (хотя и не так быстро, как формат необработанного двоичного обмена). К сожалению, хотя он является частью спецификации IEEE и C-99, он плохо поддерживается компилятором MSVC (хотя теперь это может измениться и является частью C++).

2

Посмотрите на сайт бинарной переносимости. https://github.com/MalcolmMcLean/ieee754

Функция записи порта IEEE 754 довольно длинна, но это всего лишь вырезать и вставлять задания. Существует также плавающая версия.

/* 
* write a double to a stream in ieee754 format regardless of host 
* encoding. 
* x - number to write 
* fp - the stream 
* bigendian - set to write big bytes first, elee write litle bytes 
*    first 
* Returns: 0 or EOF on error 
* Notes: different NaN types and negative zero not preserved. 
*   if the number is too big to represent it will become infinity 
*   if it is too small to represent it will become zero. 
*/ 
int fwriteieee754(double x, FILE *fp, int bigendian) 
{ 
    int shift; 
    unsigned long sign, exp, hibits, hilong, lowlong; 
    double fnorm, significand; 
    int expbits = 11; 
    int significandbits = 52; 

    /* zero (can't handle signed zero) */ 
    if (x == 0) 
    { 
     hilong = 0; 
     lowlong = 0; 
     goto writedata; 
    } 
    /* infinity */ 
    if (x > DBL_MAX) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     lowlong = 0; 
     goto writedata; 
    } 
    /* -infinity */ 
    if (x < -DBL_MAX) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     hilong |= (1 << 31); 
     lowlong = 0; 
     goto writedata; 
    } 
    /* NaN - dodgy because many compilers optimise out this test, but 
    *there is no portable isnan() */ 
    if (x != x) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     lowlong = 1234; 
     goto writedata; 
    } 

    /* get the sign */ 
    if (x < 0) { sign = 1; fnorm = -x; } 
    else { sign = 0; fnorm = x; } 

    /* get the normalized form of f and track the exponent */ 
    shift = 0; 
    while (fnorm >= 2.0) { fnorm /= 2.0; shift++; } 
    while (fnorm < 1.0) { fnorm *= 2.0; shift--; } 

    /* check for denormalized numbers */ 
    if (shift < -1022) 
    { 
     while (shift < -1022) { fnorm /= 2.0; shift++; } 
     shift = -1023; 
    } 
    /* out of range. Set to infinity */ 
    else if (shift > 1023) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     hilong |= (sign << 31); 
     lowlong = 0; 
     goto writedata; 
    } 
    else 
     fnorm = fnorm - 1.0; /* take the significant bit off mantissa */ 

    /* calculate the integer form of the significand */ 
    /* hold it in a double for now */ 

    significand = fnorm * ((1LL << significandbits) + 0.5f); 


    /* get the biased exponent */ 
    exp = shift + ((1 << (expbits - 1)) - 1); /* shift + bias */ 

    /* put the data into two longs (for convenience) */ 
    hibits = (long)(significand/4294967296); 
    hilong = (sign << 31) | (exp << (31 - expbits)) | hibits; 
    x = significand - hibits * 4294967296; 
    lowlong = (unsigned long)(significand - hibits * 4294967296); 

writedata: 
    /* write the bytes out to the stream */ 
    if (bigendian) 
    { 
     fputc((hilong >> 24) & 0xFF, fp); 
     fputc((hilong >> 16) & 0xFF, fp); 
     fputc((hilong >> 8) & 0xFF, fp); 
     fputc(hilong & 0xFF, fp); 

     fputc((lowlong >> 24) & 0xFF, fp); 
     fputc((lowlong >> 16) & 0xFF, fp); 
     fputc((lowlong >> 8) & 0xFF, fp); 
     fputc(lowlong & 0xFF, fp); 
    } 
    else 
    { 
     fputc(lowlong & 0xFF, fp); 
     fputc((lowlong >> 8) & 0xFF, fp); 
     fputc((lowlong >> 16) & 0xFF, fp); 
     fputc((lowlong >> 24) & 0xFF, fp); 

     fputc(hilong & 0xFF, fp); 
     fputc((hilong >> 8) & 0xFF, fp); 
     fputc((hilong >> 16) & 0xFF, fp); 
     fputc((hilong >> 24) & 0xFF, fp); 
    } 
    return ferror(fp); 
} 
Смежные вопросы