2017-02-03 11 views
2

Я пытаюсь написать программу на C, которая читает двоичный файл и преобразует его в тип данных. Я создаю двоичный файл с головной командой head -c 40000 /dev/urandom > data40.bin. Программа работает для типов данных int и char, но не работает для двойного. Вот код программы.Чтение из двоичного файла и преобразование в double?

void double_funct(int readFrom, int writeTo){ 
    double buffer[150]; 
    int a = read(readFrom,buffer,sizeof(double)); 
    while(a!=0){ 
     int size = 1; 
     int c=0; 

     for(c=0;c<size;c++){ 
      char temp[100]; 
      int x = snprintf(temp,100,"%f ", buffer[c]); 
      write(writeTo, temp, x); 
     } 
     a = read(readFrom,buffer,sizeof(double)); 
    } 
} 

и это функция, которая работает голец

void char_funct(int readFrom, int writeTo){ 
    char buffer[150]; 
    int a = read(readFrom,buffer,sizeof(char)); 
    while(a!=0){ 
     int size = 1; 
     int c=0; 

     for(c=0;c<size;c++){ 
      char temp[100]=" "; 
      snprintf(temp,100,"%d ", buffer[c]); 
      write(writeTo, temp, strlen(temp)); 
     } 
     a = read(readFrom,buffer,sizeof(char)); 
    } 
} 

Проблема заключается в том, что с полукокса мне нужно, чтобы получить 40000 слов с wc -w file и я получаю их. Теперь с двойным я получаю случайное количество слов, но теоретически я должен получить 5000 из 40000 байт данных, но я получаю случайное количество между 4000 и 15000, а для char я получаю 40000, как будто он должен 1 байт для одного символа.

Я не знаю, что не так, тот же код работает для int, где я получаю 10000 слов из 40000 байт данных.

+0

Небезопасно предполагать, что 'read()' считывает полное количество запрошенных байтов. Крайне важно использовать возвращаемое значение, чтобы определить, сколько байтов действительно было прочитано. –

+0

Кроме того, 'read()' вернет '-1', если возникнет ошибка. Вы не учитываете эту возможность, и если произошла ошибка, вы обрабатываете данные who-know-what. –

+1

Эта строка: 'int x = snprintf (temp, 100,"% f ", buffer [c]);' принимает один символ из буфера и пытается преобразовать его в float.Возможно, вы захотите проверить возвращаемое значение 'x', которое сообщит вам, сколько было записано в буфер. Но это, конечно, не то, что вы хотите делать. – bruceg

ответ

5

Основная проблема заключается в том, что массив temp не достаточно велик для формата и данных printf. IEEE-754 double s имеют десятичный индекс экспоненциального диапазона от -308 до +308. Вы печатаете свои двойники с форматом "%f", который производит простое десятичное представление. Поскольку точность не указана, применяется точность по умолчанию 6. Это может потребовать до 1 (знак) + 309 (цифр) + 1 (десятичная точка) + 6 (конечные десятичные знаки) + 1 (терминатор) символов (всего 318), но у вас есть только место для 100.

Вы печатаете в буфере с помощью snprintf(), и, следовательно, не захвачена за пределы массива, но snprintf() возвращает число байтов, которые бы требовались, меньше той, что требуется для терминатора. Это число байтов, которое вы write(), и во многих случаях, что делает overrun ваш буфер. Вы видите результат в своем выходе.

Во-вторых, вы также увидите большое количество 0.00000 в своем выходе, возникающее из округления небольших чисел до 6-значных цифр.

Возможно, вам удастся добиться большего успеха, если вы измените формат, с которого вы печатаете цифры. Например, "%.16e " даст вам результат в экспоненциальном формате с 17 значащими цифрами (один над десятичной точкой). Это не потребует чрезмерного пространства в памяти или на диске, и оно будет точно передавать все числа независимо от масштаба, предположив, что ваши double s представлены в IEEE 754. Если вы хотите, вы также можете устранить (довольно безопасное) предположение формата IEEE 754, используя вариант, предложенный @chux в комментариях. Это был бы самый безопасный подход.

Еще одна вещь: плавающая точка IEEE поддерживает бесконечность и множество значений, отличных от числа. Их очень мало по сравнению с обычными номерами FP, но по-прежнему возможно, что вы случайно нажмете на один из них. Вероятно, они будут преобразованы в вывод, но вы можете подумать о том, нужно ли им специально заниматься с ними.

+0

Спасибо, что это работает! – simon44556

+0

Примечание: Отключено на 1. Нужен 'buffer [318]'. -> "309 (цифры)" для '1e308'. – chux

+3

''% .15e "' хорошо, но этого недостаточно, чтобы напечатать несколько разных 'double' как другой текст. Предложите 'printf ("%. * E \ n ", DBL_DECIMAL_DIG - 1, buffer [c]);' чтобы печатать все разные 'double' по-разному. Или 'printf ("% a \ n ", buffer [c]);' – chux

Смежные вопросы