2014 dxdy logo

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

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




Начать новую тему Ответить на тему На страницу 1, 2, 3, 4  След.
 
 Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 16:25 


20/10/12
235
Добрый день, уважаемые участники форума!
Сейчас я занимаюсь написанием программы-калькулятора(для выражений вроде 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 


05/09/12
2587
С сыном как раз пишем такую программу. Я планирую реализовать функцию, которая принимает на вход операцию - умножение, деление, степень и т.п., и возвращает результат и флаг успешности проведения операции. При этом, если операция не успешна, то сама функция выводит объяснение - деление на ноль, корень из отрицательного числа и т.п. При разборе строки она будет вызываться на каждую операцию, пока флаг успешного завершения истина, в противном случае немедленный выход из алгоритма.

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


20/10/12
235
_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 
Аватара пользователя


31/10/08
1244
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 
Заслуженный участник


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

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 20:31 
Аватара пользователя


31/10/08
1244
arseniiv в сообщении #836482 писал(а):
Насколько я помню, знак нуля с помощью сравнения на равенство в C определить нельзя, и не должно быть льзя вообще. Его можно определить по внутреннему представлению,

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

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение13.03.2014, 20:57 
Заслуженный участник


28/04/09
1933
По стандарту 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 
Аватара пользователя


22/09/09

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

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 11:58 
Заслуженный участник
Аватара пользователя


19/12/10
1546
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 
Заслуженный участник
Аватара пользователя


19/12/10
1546
Также и С-код
Используется синтаксис C
if(!x);
(где x имеет тип double)

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

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 14:46 
Заслуженный участник


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

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение14.03.2014, 15:40 
Заслуженный участник


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

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение15.03.2014, 16:08 
Заслуженный участник


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

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение15.03.2014, 16:59 


01/12/11

1047
Анализ деления на 0 можно возложить на саму систему. При делении на 0 система выдаст ошибку с определённым кодом. На этот код в программе формируется соответствующее сообщение.

 Профиль  
                  
 
 Re: Вопросы по реализации калькулятора. Си.
Сообщение15.03.2014, 17:40 
Заслуженный участник


02/08/11
6874
Просто проверять каждый промежуточный результат. Как только получили Infinity или NaN - выдавать ошибку с учётом последней операции (если деление - "деление на нуль", если тангенс - "не определён", ну и т. д.).

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 51 ]  На страницу 1, 2, 3, 4  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group