Минимальная длина буфера для чтения с плавающей запятой

Я пишу небольшую программу командной строки, которая считывает два числа с плавающей запятой, целое число и небольшую строку (максимум 4 символа) из стандартного ввода. Я пытаюсь выяснить размер буфера, который я должен создать и передать fgets. Я подумал, что могу рассчитать это на основе того, сколько цифр должно быть включено в максимальные значения float и int соответственно, например:

#include <float.h>
#include <limits.h>

...

int fmax = log10(FLOAT_MAX) + 2;     // Digits plus - and .
int imax = log10(INT_MAX) + 1;       // Digits plus -
int buflen = 4 + 2*fmax + imax + 4;  // 4 chars, 2 floats, 1 int, 3 spaces and \n

...

fgets(inbuf, buflen + 1, stdin);

Но мне пришло в голову, что на самом деле это может быть не так. В моей системе imax оказывается равным 10, что кажется немного низким, а fmax — 40. (Что, как мне кажется, немного многовато, учитывая, что более длинные значения могут быть представлены с помощью нотации e.)

Итак, мой вопрос: это лучший способ решить эту проблему? Это вообще необходимо? Просто это кажется более элегантным, чем назначать буфер размером 256 и предполагать, что этого будет достаточно. Назовите это вопросом гордости ;P.


person Daniel Buckmaster    schedule 15.09.2011    source источник
comment
Пожалуйста, выберите между C и C++, когда задаете свой вопрос. Ответы будут разными, в зависимости от того, какой язык вы используете.   -  person Robᵩ    schedule 15.09.2011
comment
Почему вы хотите использовать разные буферы для int и float? Вам все равно придется читать их один за другим, поэтому возьмите буфер, достаточно большой, чтобы вместить поплавок, и покончите с этим.   -  person arne    schedule 15.09.2011
comment
Кроме того: взгляните на INT_MAX и убедитесь, что он действительно имеет только десять цифр или около того в представлении по основанию 10. Затем подумайте о двойном (64-битном, с 53-битной мантиссе), и вы, вероятно, увидите, что 40 цифр звучат примерно правильно.   -  person arne    schedule 15.09.2011
comment
Если 1 ГБ стоит 10 долларов, каждый байт стоит 0,000001 цента. Если минимальная заработная плата составляет 6 долларов в час или 0,001667 центов в секунду. Точка, в которой ваше время стоит больше, чем память, которую вы экономите, составляет около 0,6 мс на байт. Иными словами, если вы не экономите 1,5 КБ в секунду, возможно, вы зря тратите время. Если, сэкономив несколько байтов, вы создадите ошибку, это может стоить вам гораздо дороже. Память можно использовать повторно, ваше время — нет. ;)   -  person Peter Lawrey    schedule 15.09.2011
comment
@ Питер, ты забываешь о факторе обсессивно-компульсивного расстройства :)   -  person Jeremy Friesner    schedule 15.09.2011
comment
@ Джереми, я согласен, трудно писать расточительный код. В какой-то момент вы должны осознать, что это академическое упражнение.   -  person Peter Lawrey    schedule 16.09.2011
comment
Роб: Извините, исправлено. Я полагал, что ответ будет одинаковым для обоих языков, хотя очевидно, что C++ имеет в своем распоряжении больше разных инструментов. arne: Я использую поплавок, а не двойную, а справедливую точку. Питер: Джереми прав (или неправ... но прав ;P), хотя меня очень интересует встраиваемое программирование... следовательно, я скуп на память :P.   -  person Daniel Buckmaster    schedule 19.09.2011
comment
@Daniel: Пожалуйста, используйте синтаксис уведомления @user.   -  person Lightness Races in Orbit    schedule 19.09.2011
comment
@Tomalak Это не позволило бы мне пометить нескольких пользователей, и я не хотел играть в избранное. Должен ли я в будущем разделять такие вещи на несколько комментариев?   -  person Daniel Buckmaster    schedule 19.09.2011
comment
@Daniel: я так думаю, да. Гораздо лучше, чем писать комментарии, о которых никто не узнает. (Совет: вы можете обойти ограничение на глупые уведомления, используя хотя бы один ` в своем комментарии: D)   -  person Lightness Races in Orbit    schedule 19.09.2011
comment
@Peter: Ваш анализ ошибочен. Если ваш код запущен на миллиарде устройств, каждый сохраненный байт сохраняется на каждом таком устройстве, то есть каждый сохраненный байт стоит 1000 долларов. Это довольно большой. :-)   -  person R.. GitHub STOP HELPING ICE    schedule 11.10.2011
comment
@R.. Анализ предназначен только для того, чтобы дать чувство меры. Однако вы, похоже, используете мой аргумент, чтобы найти точку, в которой он имеет значение. Конечно, память на некоторых устройствах стоит намного больше, чем на других, особенно. если вы не можете обновить их, потому что они не ваши. :-)   -  person Peter Lawrey    schedule 12.10.2011


Ответы (4)


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

Если вы хотите быть ленивым, просто выберите большое число, например 1000...

person R.. GitHub STOP HELPING ICE    schedule 15.09.2011

Это определено для чисел с плавающей запятой с основанием 10 (#include <float.h> или эквивалентный член std::numeric_limits<float_type>):

FLT_MAX_10_EXP // for float
DBL_MAX_10_EXP // for double
LDBL_MAX_10_EXP // for long double

Как и максимальная точность для десятичных знаков в базе 10:

FLT_DIG // for float
DBL_DIG // for double
LDBL_DIG  // for long double

Хотя это действительно зависит от того, что вы определяете как допустимое число с плавающей запятой. Можно представить, что кто-то ожидает:

00000000000000000000000000000000000000000000000000.00000000000000000000

считываться как ноль.

person MSN    schedule 15.09.2011
comment
Честная оценка. Я никогда не думал, что смогу учитывать каждый технически правильный ввод, хотя я думаю, что лучше учитывать как можно больше разумных вводов, которые можно сократить, экономя на размере буфера. - person Daniel Buckmaster; 19.09.2011

Я уверен, что есть хороший способ определить максимальную длину строки с плавающей запятой алгоритмически, но что в этом интересного? Разберемся грубой силой!

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int, char **)
{
   float f;
   unsigned int i = -1;
   if (sizeof(f) != sizeof(i))
   {
      printf("Oops, wrong size!  Change i to a long or whatnot so the sizes match.\n");
      return 0;
   }
   printf("sizeof(float)=%li\n", sizeof(float));

   char maxBuf[256] = "";
   int maxChars = 0;
   while(i != 0)
   {
      char buf[256];
      memcpy(&f, &i, sizeof(f));
      sprintf(buf, "%f", f);
      if ((i%1000000)==0) printf("Calclating @ %u: buf=[%s] maxChars=%i (maxBuf=[%s])\n", i, buf, maxChars, maxBuf);
      int numChars = strlen(buf);
      if (numChars > maxChars)
      {
         maxChars = numChars;
         strcpy(maxBuf, buf);
      }
      i--;
   }
   printf("Max string length was [%s] at %i chars!\n", maxBuf, maxChars);
}

Похоже, что ответ может быть 47 символов на число с плавающей запятой (по крайней мере, на моей машине), но я не собираюсь доводить его до конца, так что, возможно, больше.

person Jeremy Friesner    schedule 15.09.2011

Следуя ответу @MSN, вы не можете знать, что ваш буфер достаточно велик.

Рассмотреть возможность:

const int size = 4096;
char buf[size] = "1.";
buf[size -1 ] = '\0';
for(int i = 2; i != size - 1; ++i)
    buf[i] = '0';
double val = atof(buf);
std::cout << buf << std::endl;
std::cout << val << std::endl;

Здесь atof() обрабатывает (как и предполагалось) тысячесимвольное представление 1.

Так что на самом деле вы можете сделать одно или несколько из следующих действий:

  • Обработать случай отсутствия достаточно большого буфера
  • Иметь лучший контроль над входным файлом
  • Используйте fscanf напрямую, чтобы сделать размер буфера чужой проблемой
person Keith    schedule 15.09.2011