2014 dxdy logo

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

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




Начать новую тему Ответить на тему На страницу 1, 2  След.
 
 Деление на 0
Сообщение23.07.2014, 13:19 
Аватара пользователя


30/05/09
121
Киев
Всем привет! Вопрос собственно о корректности математических действий. В частном случае это деление на ноль или достаточно близкое к нулю число, которое приводит к вылету программы. В Delphi строю логику на обработке исключений:
Используется синтаксис Delphi
procedure Proc1();
var a,b,c: Double;
begin
//...
   try
      a := b / c;
   excpet
      on EMathError do a := -1;
   end;
end;
 

Стоит отметить, что класс исключений EMathError хорош тем, что перехватывает все математические исключения: деление на 0, переполнение (логарифм нуля или числа достаточно близкого к нулю) и пр.
Возможно ли реализовать на С/С++ нечто подобное? Решение типа cath(...) не работает.
Используется синтаксис C++
void Proc1()
{
   double a, b, c;
   //...
   try
   {
      a = b / c;
   }
   catch (...)
   {
      a = -1.0;
   }
}
 

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 14:08 
Заслуженный участник
Аватара пользователя


28/07/09
1238
С double вы же можете просто проверить результат на INF и NaN. Некорректные математические действия приводят к ним.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 14:38 
Заслуженный участник
Аватара пользователя


30/01/06
72407
http://en.cppreference.com/w/cpp/numeric/fenv в самом низу.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 15:04 
Заслуженный участник


28/04/09
1933
Legioner93 в сообщении #889652 писал(а):
С double вы же можете просто проверить результат на INF и NaN.
А еще лучше проверить на корректность делитель:
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <iostream>
#include <stdexcept>

double divide(double x, double y)
{
    if (y == 0.0)
    {
        throw std::invalid_argument("Division by zero!");
    }
   
    return x / y;
}

double f(double x, double y)
{
    double q;
       
    try
    {
        q = divide(x, y);
    }
    catch (...)
    {
        q = -1.0;
    }
   
    return q;
}

int main()
{
    std::cout << f(10.0, 2.0) << std::endl;
    std::cout << f(10.0, 0.0) << std::endl;
}
Legioner93 в сообщении #889652 писал(а):
Некорректные математические действия приводят к ним.
По стандарту IEEE 754 деление на ноль $\text{---}$ вполне корректное математическое действие. :wink:

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 15:08 
Заслуженный участник
Аватара пользователя


30/01/06
72407
EtCetera в сообщении #889681 писал(а):
По стандарту IEEE 754 деление на ноль $\text{---}$ вполне корректное математическое действие.

По стандарту IEEE 754 деление на ноль может считаться как корректным, так и некорректным, в зависимости от настройки FPU.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 15:27 
Заслуженный участник


28/04/09
1933
Munin в сообщении #889686 писал(а):
По стандарту IEEE 754 деление на ноль может считаться как корректным, так и некорректным, в зависимости от настройки FPU.
Да, фразу "по умолчанию" я забыл вставить.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 15:28 
Заслуженный участник
Аватара пользователя


28/07/09
1238
EtCetera в сообщении #889681 писал(а):
Legioner93 в сообщении #889652 писал(а):
С double вы же можете просто проверить результат на INF и NaN.
А еще лучше проверить на корректность делитель

Ну, насчёт лучше вы бы так категорично не заявляли... Оверхед же. Всё зависит от ситуации.
NaN и INF достаточно удобные, чтобы их использовать как раз в подобных целях, хотя бы иногда.
Вот целочисленное деление на 0 - оно да, программу останавливает.
EtCetera в сообщении #889681 писал(а):
Legioner93 в сообщении #889652 писал(а):
Некорректные математические действия приводят к ним.
По стандарту IEEE 754 деление на ноль $\text{---}$ вполне корректное математическое действие. :wink:

Корректность я имел в виду с точки зрения математики.

-- Ср июл 23, 2014 16:30:01 --

EtCetera
Munin
Вы сейчас про какое деление-то? Если в int, так оно UB. А если в double, то что означает термин некорректность?

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 15:35 
Заслуженный участник


28/04/09
1933
Legioner93 в сообщении #889701 писал(а):
EtCetera в сообщении #889681 писал(а):
Legioner93 в сообщении #889652 писал(а):
С double вы же можете просто проверить результат на INF и NaN.
А еще лучше проверить на корректность делитель
Ну, насчёт лучше вы бы так категорично не заявляли... Оверхед же.
Если производить проверку до выполнения операции, то накладные расходы есть, а если после $\text{---}$ их уже нет?
Я уж не говорю о том, что операции с NaN'ами на многих процессорах отличаются катастрофической медлительностью.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 15:51 
Заслуженный участник
Аватара пользователя


28/07/09
1238
EtCetera в сообщении #889706 писал(а):
Если производить проверку до выполнения операции, то накладные расходы есть, а если после $\text{---}$ их уже нет?
Я уж не говорю о том, что операции с NaN'ами на многих процессорах отличаются катастрофической медлительностью.

Давайте сразу определимся, о какой ситуации мы говорим?
О простом делении $a$ на $b$ прямо по месту (без вызова функции!), с использованием результата тут и сразу? Например для печати, или для чего-то другого, но не как промежуточный результат для последующих вычислений?
Тогда особой разницы нет.
Но я, отвечая вам, смотрел на приведенный код. И там вы завернули деление в функцию. При таком подходе разница есть. Напишу, если хотите.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 16:00 
Заслуженный участник
Аватара пользователя


30/01/06
72407
Legioner93 в сообщении #889701 писал(а):
Вы сейчас про какое деление-то? Если в int, так оно UB.

По POSIX, деление на 0 в int вызывает тот же SIGFPE, что и fp исключения. Это где-то на StackOverflow было упомянуто.

EtCetera в сообщении #889706 писал(а):
Я уж не говорю о том, что операции с NaN'ами на многих процессорах отличаются катастрофической медлительностью.

Ничё себе. Это интересно. Они же, как раз, проще: garbage in, garbage out. Чего именно вы меряли, и на каких процессорах?

EtCetera в сообщении #889706 писал(а):
Если производить проверку до выполнения операции, то накладные расходы есть, а если после $\text{---}$ их уже нет?

Их может быть просто меньше. Допустим, у вас формула из 8 входных значений, тогда вы должны в одном случае проделать 15 проверок, а в другом - всего одну, в самом конце.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 16:30 
Аватара пользователя


30/05/09
121
Киев
В данном случае речь идёт о вещественных числах. Насколько я понял FPU входит в CPU. И существует два режима работы FPU:
1. С генерацией исключений (который вроде как по-умолчанию);
2. Без генерации исключений с константами типа INF(1.0/0.0) и NaN(0.0/0.0);
Последний режим действительно работает быстрее, как сообщается в Embarcadero RAD Studio Help:
Цитата:
When using OpenGL to render 3D graphics, we recommend that you disable all floating-point exceptions for performance reasons. To do this, call Set8087CW(0x133f) in...

Set8087CW задаёт контрольное слово (control word) FPU, которое задаёт маску исключений FPU при работе с вещественными числами. В общем попробовал. Работает:
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <stdio.h>
#include <conio.h>
#include <System.hpp>
#include <Math.hpp>

void GetDouble(const char *text, double *pValue)
{
        printf(text);
        scanf("%lf", pValue);
        fflush(stdin);
}
//-----------------------------------------------------------------------------
int main()
{
        double a, b, c;

        Set8087CW(0x133F);

        //получаем данные
        GetDouble("a= ", &a);
        GetDouble("b= ", &b);

        //расчет
        c = a / b;

        //вывод данных
        if (IsInfinite(c))
                printf("Infinite result\n");
        else if (IsNan(c))
                printf("Not a number\n");
        else
                printf("c= %G\n", c);

        printf("Press any key to exit...");
        _getch();
        return 0;
}
//-----------------------------------------------------------------------------
 


-- Ср июл 23, 2014 17:08:10 --

Справедливости ради, стоит отметить, что под Windows многие компиляторы реализуют так называемую структурную обработку исключений (SEH - structure exception handling), которая хорошо описана у Назара и Рихтера. Поэтому на С++ ошибки можно обрабатывать с помощью SEH:
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <stdio.h>
#include <conio.h>
#include <windows.h>

void GetDouble(const char *text, double *pValue)
{
        printf(text);
        scanf("%lf", pValue);
        fflush(stdin);
}
//-----------------------------------------------------------------------------
int main()
{
        double a, b, c;
        bool calced = false;

        //получаем данные
        GetDouble("a= ", &a);
        GetDouble("b= ", &b);

        //расчет
        __try
        {
                c = a / b;
                calced = true;
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
                printf("MATH ERROR.\n");
        }

        //вывод данных
        if (calced)
                printf("c= %G\n", c);+

        printf("Press any key to exit...");
        _getch();
        return 0;
}
//-----------------------------------------------------------------------------
 

Таким образом есть два взаимоисключающих варианта обработки мат. ошибок:
1. Использование SEH;
2. Использование сравнения на INF, NaN (для этого надо настроить FPU, чтобы он не выбрасывал исключений).
Что лучше?

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 17:13 
Заслуженный участник


28/04/09
1933
Legioner93
Legioner93 в сообщении #889714 писал(а):
При таком подходе разница есть. Напишу, если хотите.
Пишите, конечно.

Munin
Munin в сообщении #889720 писал(а):
Они же, как раз, проще: garbage in, garbage out. Чего именно вы меряли, и на каких процессорах?
До последнего времени операции по работе с NaN'ами, бесконечностями и денормализованными числами процессоры одной небезызвестной фирмы на букву "i" выполняли в сотни-тысячи раз медленнее, чем операции с обычными нормализованными числами (некоторое время назад ситуация постепенно начала выправляться, но окончательно вопрос еще не решен). Забавно, что у процессоров другой небезызвестной фирмы на букву "a" таких проблем не наблюдалось.
Сходу найденные ссылки:
Munin в сообщении #889720 писал(а):
Допустим, у вас формула из 8 входных значений, тогда вы должны в одном случае проделать 15 проверок, а в другом - всего одну, в самом конце.
Возможно, однако с учетом вышеприведенной особенности работы с NaN'ами может выйти и медленнее.

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 17:40 
Заслуженный участник
Аватара пользователя


28/07/09
1238
EtCetera
В общем, при таком подходе типа "функции - чёрного ящика" уместно (как минимум, для некоторых сценариев) делать следующее:
1) Максимально освободить функцию от разнообразных проверок, насколько это позволяет сделать язык. C позволяет нам не думать о разнообразных математических действиях с переменными double, услужливо предоставляя inf-nan-механизм. Подал пользователь на вход некорректные числа - его проблема, вернём ему inf или nan.
При этом функция
а) не упадёт на некорректных значениях, в то же время
б) будет иметь максимальную скорость работы при корректных данных. По-моему, идеальное сочетание.

1') Многие вообще забивают на пункт а) (в других случаях). Это же всё-таки C. Но тут этот пункт выполняется - и очень хорошо.

2) А что же должен проверять сам юзер: аргументы или возвращаемое значение?
+ Возвращаемое значение проверять как-то более идейно что ли. Фактически это тот же код возврата для математических функций.
+ При большом количестве аргументов проверить одно-единственное значение на NaN всё-таки будет быстрее.
- В то же время, если функция принимает так много аргументов, то наверное и работает она не быстро. После выявления же некорректных аргументов мы её не будем запускать вообще.
- Проверяя аргументы, мы более прицельно можем идентифицировать ошибку входных данных (один-единственный NaN или INF не очень многословен)

 Профиль  
                  
 
 Re: Деление на 0
Сообщение23.07.2014, 19:02 
Заслуженный участник
Аватара пользователя


30/01/06
72407
EtCetera в сообщении #889745 писал(а):
До последнего времени операции по работе с NaN'ами, бесконечностями и денормализованными числами процессоры одной небезызвестной фирмы на букву "i" выполняли в сотни-тысячи раз медленнее, чем операции с обычными нормализованными числами

Простите, а какой смысл имеет валить в одну кучу первые две и третью разновидность аргументов?

 Профиль  
                  
 
 Re: Деление на 0
Сообщение24.07.2014, 00:00 
Заслуженный участник


28/04/09
1933
Legioner93
Legioner93 в сообщении #889746 писал(а):
При большом количестве аргументов проверить одно-единственное значение на NaN всё-таки будет быстрее.
Все зависит от того, насколько часто возвращаемое значение оказывается NaN'ом (по крайней мере, на Intel'овских процессорах). Если достаточно часто (в процентном отношении), то может оказаться и наоборот.

Munin
Munin в сообщении #889751 писал(а):
EtCetera в сообщении #889745 писал(а):
До последнего времени операции по работе с NaN'ами, бесконечностями и денормализованными числами процессоры одной небезызвестной фирмы на букву "i" выполняли в сотни-тысячи раз медленнее, чем операции с обычными нормализованными числами
Простите, а какой смысл имеет валить в одну кучу первые две и третью разновидность аргументов?
В какую кучу, простите?

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

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



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

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


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

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