2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу 1, 2, 3, 4  След.
 
 Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 16:25 
Добрый день, уважаемые участники форума!
Сейчас я занимаюсь написанием программы-калькулятора(для выражений вроде 2^2+3*(6-4)/5.2) и у меня возникли некоторые трудности, с разрешением которых я прошу вас мне помочь.
Программа должна адекватно реагировать на исключения, и то первое, с чем я столкнулся - это деление на ноль.
Предположим у нас есть зверская функция для замены произведений и частных в строке(её досконально проверять нет особой необходимости, она здесь просто для ясности):
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
char *remove_md(char *expression)
{
  char *p = expression, *left, *right, op1, op2, *DOUBLE;     //BUFFER FOR double
  int i = 0, shift;
  double result, right_op;

  op1 = '*', op2 = '/';

  while(p[i])
    if(p[i] == op1 || p[i] == op2)
    {
      if(is_null(right_op = make_double(p + i + 1, &shift)) && p[i] == op2)//(make_double в shift сохраняет  cколько символов прочитала)
      {
        //do something!!!
      }
      DOUBLE = calloc(MAX_DOUBLE, 1);
      right = p + i + shift, left = p + i - 1;
           
      while(left > p)
      {
        if((*(left - 1) == 'e' || *(left - 1) == 'E') && left > p)
          --left;
        if(isop(*left))
         break;
        --left;
      }
      if(left != p)
          ++left;

      if(p[i] == op2)
        result = bin_op(make_double(left, &shift), right_op, 4);
      else
        result = bin_op(make_double(left, &shift), right_op, 3);
      sprintf_s(DOUBLE, MAX_DOUBLE, "%.20g", result);
      p = insert(expression, left, right + 1, DOUBLE);
      expression = p;

      free(DOUBLE);
      i = 0;
    }
    else
     ++i;
  return p;
}
 

и тут у меня вопрос по непосредственной проверке числа double на равенство нулю
Как лучше всего реализовать is_null? Понятно, что проверять == не дело с double. Может что-то с битами?
Второй вопрос на будущее: что делать, когда этот 0 найдется? Дело в том, что программа довольно большая получается, для начинающего вроде меня(700-800 строк уже сейчас) и обработка ошибок сделана в самом начале(анализ формата введенного выражения). Ошибка с нулем же - совсем другого рода, она в ходе вычислений получается и нужно как-то выкарабкаться из нагромождения функций. Как тут грамотней поступить?

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 17:43 
С сыном как раз пишем такую программу. Я планирую реализовать функцию, которая принимает на вход операцию - умножение, деление, степень и т.п., и возвращает результат и флаг успешности проведения операции. При этом, если операция не успешна, то сама функция выводит объяснение - деление на ноль, корень из отрицательного числа и т.п. При разборе строки она будет вызываться на каждую операцию, пока флаг успешного завершения истина, в противном случае немедленный выход из алгоритма.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 17:46 
_Ivana, понятно, придется протаскивать флаг через функции.

-- 13.03.2014, 17:54 --

_Ivana вот такая функция сравнения корректна?
Код:
int is_null(double x)
{
  int i;
  char *p = &x;
  for(i = 0; i < sizeof(double)/sizeof(char); ++i)
    if(p[i] & 0xff)
      return 0;
  return 1;
}

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 19:20 
Аватара пользователя
shukshin в сообщении #836427 писал(а):
_Ivana вот такая функция сравнения корректна?
Код:

int is_null(double x)
{
int i;
char *p = &x;
for(i = 0; i < sizeof(double)/sizeof(char); ++i)
if(p[i] & 0xff)
return 0;
return 1;
}


Нет конечно. Есть "+0.0" и "-0.0"
Правильно сравнивать
if (x==0.0) && (x==-0.0)
А также есть NaN Inf их тоже надо корректно обрабатывать. Причем в каждой операции.
Как вариант можно ловить исключение в try catch и обрабатывать.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 19:35 
Pavia в сообщении #836472 писал(а):
Нет конечно. Есть "+0.0" и "-0.0"
Правильно сравнивать
if (x==0.0) && (x==-0.0)
Насколько я помню, знак нуля с помощью сравнения на равенство в C определить нельзя, и не должно быть льзя вообще. Его можно определить по внутреннему представлению, или если язык (какой-нибудь другой) предоставляет соответствующую функцию. Так что сравнение с минус-нулём здесь избыточно.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 20:31 
Аватара пользователя
arseniiv в сообщении #836482 писал(а):
Насколько я помню, знак нуля с помощью сравнения на равенство в C определить нельзя, и не должно быть льзя вообще. Его можно определить по внутреннему представлению,

Да похоже на то. Но выше нет проверки на знак там проверка на ноль.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 20:57 
По стандарту IEEE 754 оба нуля должны при сравнении считаться равными. Оператор == языка C действует в соответствии со стандартом. Нестандартное поведение наблюдается, к примеру, в методе Double.equals языка Java, поскольку этот метод реализован с помощью метода Double.doubleToLongBits.
Таким образом, сравнение на "чистый ноль" с помощью (x == 0.0) && (x == -0.0) совершенно избыточно (достаточно x == 0.0). Другое дело, что де-факто может понадобиться сравнение на "грязный ноль" (я не знаю, какова логика работы калькулятора), а именно сравнение на не превышение (по абсолютному значению) некоего небольшого числа: fabs(x) < eps.

-- Чт мар 13, 2014 20:59:20 --

arseniiv в сообщении #836482 писал(а):
знак нуля с помощью сравнения на равенство в C определить нельзя, и не должно быть льзя вообще
Угу. Но можно определить с помощью стандартно-библиотечного макроса signbit.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 00:12 
Аватара пользователя
ИМХО все сравнения на равенство для чисел с плавающей запятой вне зависимости от применяемого языка осуществляются по формуле $|x-y|< \varepsilon $, т.о. ИМХО нужно выбрать точность $\varepsilon$ в зависимости от применяемого формата double (стоит уточнить для конкретной реализации языка: это м.б. 8-байтное представление, но м.б. и 10-байтное, которое в некоторых реализациях некоторых языков называют extended - "10-byte Extended format" и т.д.) и записывать выражение, соответствующее формуле $|x|< \varepsilon $. Я при необходимости только первожу из Си на другие языки и не знаю, м.б. x==0.0 в каких-то реализациях и работает всегда корректно, но тогда это один из тех случаев, за которые особенно нежно "любят" Си. По второму вопросу ИМХО то же, что уже посоветовали выше - выход из обработки входного выражения, желательно с указанием причины и места в выражении, на котором произошла фатальная ошибка.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 11:58 
Аватара пользователя
EtCetera в сообщении #836545 писал(а):
Таким образом, сравнение на "чистый ноль" с помощью (x == 0.0) && (x == -0.0) совершенно избыточно (достаточно x == 0.0)

Имхо, достаточно (x == 0).

Например мой компилятор следующий код:
Используется синтаксис C
if(x == 0.0);
if(x == 0);
(где x имеет тип double)

транслирует в:
Используется синтаксис ASM
fld         qword ptr [x]  
fldz  
fucompp  
fld         qword ptr [x]  
fldz  
fucompp  

То есть приведение целочисленного нуля к вещественному выполняется во время компиляции.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 13:08 
Аватара пользователя
Также и С-код
Используется синтаксис C
if(!x);
(где x имеет тип double)

транслируется в тот же ассемблерный код
Используется синтаксис ASM
fld         qword ptr [x]  
fldz  
fucompp
Пожалуй этот вариант самый "ленивый".

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 14:46 
bin в сообщении #836667 писал(а):
ИМХО все сравнения на равенство для чисел с плавающей запятой вне зависимости от применяемого языка осуществляются по формуле $|x-y|< \varepsilon $, т.о. ИМХО нужно выбрать точность $\varepsilon$ в зависимости от применяемого формата double (стоит уточнить для конкретной реализации языка...
+1
В качестве платформозависимой оценки $\varepsilon$ иногда рекомендуется максимальное $MAXEPS$ такое, что
$(1 + MAXEPS) == 1$ .

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 15:40 
Yuri Gendelman в сообщении #836837 писал(а):
bin в сообщении #836667 писал(а):
ИМХО все сравнения на равенство для чисел с плавающей запятой вне зависимости от применяемого языка осуществляются по формуле $|x-y|< \varepsilon $, т.о. ИМХО нужно выбрать точность $\varepsilon$ в зависимости от применяемого формата double (стоит уточнить для конкретной реализации языка...
+1
В качестве платформозависимой оценки $\varepsilon$ иногда рекомендуется максимальное $MAXEPS$ такое, что
$(1 + MAXEPS) == 1$ .
А что если $x$ и $y$ изначально маленькие? Тут надо более сложную логику с относительной погрешностью.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение15.03.2014, 16:08 
venco в сообщении #836852 писал(а):
Yuri Gendelman в сообщении #836837 писал(а):
В качестве платформозависимой оценки $\varepsilon$ иногда рекомендуется максимальное $MAXEPS$ такое, что
$(1 + MAXEPS) == 1$ .
А что если $x$ и $y$ изначально маленькие? Тут надо более сложную логику с относительной погрешностью.
Потому я и написал "иногда".
Если есть физические соображения о порядке величин, их и используют.
А если нет других идей, используют стандартные приемы.
Это как в преферансе: "Хода нет - ходи с бубей".

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение15.03.2014, 16:59 
Анализ деления на 0 можно возложить на саму систему. При делении на 0 система выдаст ошибку с определённым кодом. На этот код в программе формируется соответствующее сообщение.

 
 
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение15.03.2014, 17:40 
Просто проверять каждый промежуточный результат. Как только получили Infinity или NaN - выдавать ошибку с учётом последней операции (если деление - "деление на нуль", если тангенс - "не определён", ну и т. д.).

 
 
 [ Сообщений: 51 ]  На страницу 1, 2, 3, 4  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group