Russian Qt Forum
Ноябрь 24, 2024, 05:02 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Как корректно обработать деление на 0, логарифм нуля, и т.п.  (Прочитано 10067 раз)
zzzseregazzz
Гость
« : Сентябрь 23, 2013, 16:28 »

Заметил, что в таких ситуациях (при работе с типом double) не генерируется никаких исключений.
Когда деления часто встречаются в программе, контроль каждого деления сильно загромождает код. 
QString::number применительно к результату таких операций выдает непонятные надписи вроде "inf".
Как правильно обработать такие ошибки?
Есть ли настройки компиляторов (в Windows и Linux) для того, чтобы в таких ситуациях бросались исключения?
Стоит ли написать и везде использовать класс-обёртку к базовым типам?


Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Сентябрь 23, 2013, 16:39 »

Это называется nan (not a number). Имеет свойство быстро размножаться т.к. любая опреация с ним производит только nan. Решение - проверять все аргументы, напр на ноль не делить.  Желание сачкануть (генреация исключений) в данном случае неуместно, т.к. проверка всяких граничных условий есть часть работы.
Записан
zzzseregazzz
Гость
« Ответ #2 : Сентябрь 23, 2013, 16:50 »

Если деление в каждой 2-й строке, то что тогда делать?
Проверить аргументы недостаточно, т.к. 0 может получиться в процессе вычислений.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Сентябрь 23, 2013, 17:11 »

Если деление в каждой 2-й строке, то что тогда делать?
То что все делают - проверять и подсекать значения до нужного/валидного диапазона.

Проверить аргументы недостаточно, т.к. 0 может получиться в процессе вычислений.
Значит надо сохранить напр образовавшийся делитель во временной переменной и ее провеоить.

Если вылазит nan, значит входные данные некорректны - значит надо или автоматом делать их корректными или давать пользователю отлуп с указанием что не так. А выскакивать исключением здесь неграмотно
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #4 : Сентябрь 23, 2013, 20:04 »

А ведь на форуме выкладывали библиотечку, преобразующую SIG* в исключения:)
Записан
zzzseregazzz
Гость
« Ответ #5 : Сентябрь 24, 2013, 08:08 »

То что все делают - проверять и подсекать значения до нужного/валидного диапазона.

Например как результат деления на 0 вернуть 1e+100 ? А почему не 3e+200? Нельзя некоректные данные сделать корректными.
Записан
zzzseregazzz
Гость
« Ответ #6 : Сентябрь 24, 2013, 08:11 »

Значит надо сохранить напр образовавшийся делитель во временной переменной и ее провеоить.

Если проверять каждое деление может существенно упасть производительность.
Не говоря уже о читаемости кода.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Сентябрь 24, 2013, 09:55 »

Если проверять каждое деление может существенно упасть производительность.
Не говоря уже о читаемости кода.
Я занимаюсь в основном инженерными расчетами и аккуратно проверяю всю арифметику. Либы которые я испльзую делают так же, насколько я могу судить по исходникам. Практически любой расчет имеет крайние/вырожденные случаи которые надо корректно отработать. Ничего особо страшного нет, проверки достаточно редки, бросаться на каждое деление не надо.

Напр в исходном массиве треугольников могут быть такие что их площадь слишком мала или нулевая. Да, придется их сначала отфильтровать и удалить из контейнера, иначе быстро получу nan'ы. А выбросив исключение - что дальше? С этими данными не работаем, а почему - хз. Часто только этим профессиональный софт отличается от любительского.
Записан
zzzseregazzz
Гость
« Ответ #8 : Сентябрь 24, 2013, 11:41 »

Простой пример. Надо отобразить отношение сигнал/шум в логарифмическом масштабе.
 
Что делать, если
- нет ни сигнала ни шума - log(0/0)
- сигнал есть, шум нулевой - log(1/0)
- сигнала нет, шум есть - log(0)

В первых двух случаях получаем сразу 2 неопределённости, но при этом все ситуации вполне корректны.

Как представить результат в виде строки?

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Сентябрь 24, 2013, 12:04 »

Не знаю причем здесь строка. Ввести порог шума и сигнала - достаточно малые числа. типа 1.0e-8, это должно быть известно по задаче. Ну или просто if'ами. В любом случае exception здесь неуместны, т.к. ситуация штатная, корректная (по крайней мере с точки зрения пользователя), и обработке подлежит
Записан
zzzseregazzz
Гость
« Ответ #10 : Сентябрь 24, 2013, 12:25 »

Т.е. из 0 делаем 1.0e-8. При этом считаем в несколько этапов - вместо
Код
C++ (Qt)
double sn=log(s/n);
пишем
Код
C++ (Qt)
double n_=((n<=0)?1e-8:n);
double sn_=s/n_;
double sn__=((sn_<=0)?1e-8:sn_);
double sn=log(sn__);
Боюсь, что это не пройдет code review из-за большого объёма и плохой читаемости, даже без учета логики (а если написать через if, то тем более).

Пусть пришел сигнал 1e-8, а шум нулевой.
Вместо 0 подставляем 1e-8. т.е. получается, что сигнал равен шуму - а это совсем неправильно.
« Последнее редактирование: Сентябрь 24, 2013, 12:51 от zzzseregazzz » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #11 : Сентябрь 24, 2013, 13:28 »

Да прибавьте в числителе и знаменателе (к шуму и сигналу) малую величину epsilon (cut-off)

Код
C++ (Qt)
static const double epsilon = 0.000001;
 
double sn = log((s + epsilon)/(n + epsilon));
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Сентябрь 24, 2013, 13:33 »

Код
C++ (Qt)
double sn=log(s/n);
пишем
Код
C++ (Qt)
double n_=((n<=0)?1e-8:n);
double sn_=s/n_;
double sn__=((sn_<=0)?1e-8:sn_);
double sn=log(sn__);
Боюсь, что это не пройдет code review из-за большого объёма и плохой читаемости,
Точно не пройдет, но никто не мешает записать это нормально, напр
Код
C++ (Qt)
double signal = GetSignal();
double noise = GetNoise();
if (Max(signal. noise) < fudge_Factor)
result = 0;
else {
double ratio = Clamp(signal, signal_Min, signal_Max) / Clamp(noise. noise_Min, noise_Max);
result = log(ratio);
}
Записан
zzzseregazzz
Гость
« Ответ #13 : Сентябрь 24, 2013, 16:50 »

Ну вот добрались уже до 8 строчек вместо одной.
К тому же следует учесть, что все функции теоретически могут бросить исключение, и должны быть обернуты в try{}catch{}.
« Последнее редактирование: Сентябрь 24, 2013, 16:54 от zzzseregazzz » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Сентябрь 24, 2013, 18:05 »

Ну вот добрались уже до 8 строчек вместо одной.
Не надо стремиться написать так
Код:
double sn_=s/n_;
Так пишут женщины - и то не все  Улыбающийся

К тому же следует учесть, что все функции теоретически могут бросить исключение, и должны быть обернуты в try{}catch{}.
Ни разу не видел чтобы напр sqrt, log. acos и.т.п. вызывали исключение. Дал некорректный аргумент - получил nan, вот и вся любовь. А если мы хотим идти своим путем, то проще бросить так
Код
C++ (Qt)
if (sn != sn) throw;
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.366 секунд. Запросов: 23.