Название: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 23, 2013, 16:28 Заметил, что в таких ситуациях (при работе с типом double) не генерируется никаких исключений.
Когда деления часто встречаются в программе, контроль каждого деления сильно загромождает код. QString::number применительно к результату таких операций выдает непонятные надписи вроде "inf". Как правильно обработать такие ошибки? Есть ли настройки компиляторов (в Windows и Linux) для того, чтобы в таких ситуациях бросались исключения? Стоит ли написать и везде использовать класс-обёртку к базовым типам? Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 23, 2013, 16:39 Это называется nan (not a number). Имеет свойство быстро размножаться т.к. любая опреация с ним производит только nan. Решение - проверять все аргументы, напр на ноль не делить. Желание сачкануть (генреация исключений) в данном случае неуместно, т.к. проверка всяких граничных условий есть часть работы.
Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 23, 2013, 16:50 Если деление в каждой 2-й строке, то что тогда делать?
Проверить аргументы недостаточно, т.к. 0 может получиться в процессе вычислений. Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 23, 2013, 17:11 Если деление в каждой 2-й строке, то что тогда делать? То что все делают - проверять и подсекать значения до нужного/валидного диапазона.Проверить аргументы недостаточно, т.к. 0 может получиться в процессе вычислений. Значит надо сохранить напр образовавшийся делитель во временной переменной и ее провеоить. Если вылазит nan, значит входные данные некорректны - значит надо или автоматом делать их корректными или давать пользователю отлуп с указанием что не так. А выскакивать исключением здесь неграмотно Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Авварон от Сентябрь 23, 2013, 20:04 А ведь на форуме выкладывали библиотечку, преобразующую SIG* в исключения:)
Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 24, 2013, 08:08 То что все делают - проверять и подсекать значения до нужного/валидного диапазона. Например как результат деления на 0 вернуть 1e+100 ? А почему не 3e+200? Нельзя некоректные данные сделать корректными. Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 24, 2013, 08:11 Значит надо сохранить напр образовавшийся делитель во временной переменной и ее провеоить. Если проверять каждое деление может существенно упасть производительность. Не говоря уже о читаемости кода. Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 24, 2013, 09:55 Если проверять каждое деление может существенно упасть производительность. Я занимаюсь в основном инженерными расчетами и аккуратно проверяю всю арифметику. Либы которые я испльзую делают так же, насколько я могу судить по исходникам. Практически любой расчет имеет крайние/вырожденные случаи которые надо корректно отработать. Ничего особо страшного нет, проверки достаточно редки, бросаться на каждое деление не надо. Не говоря уже о читаемости кода. Напр в исходном массиве треугольников могут быть такие что их площадь слишком мала или нулевая. Да, придется их сначала отфильтровать и удалить из контейнера, иначе быстро получу nan'ы. А выбросив исключение - что дальше? С этими данными не работаем, а почему - хз. Часто только этим профессиональный софт отличается от любительского. Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 24, 2013, 11:41 Простой пример. Надо отобразить отношение сигнал/шум в логарифмическом масштабе.
Что делать, если - нет ни сигнала ни шума - log(0/0) - сигнал есть, шум нулевой - log(1/0) - сигнала нет, шум есть - log(0) В первых двух случаях получаем сразу 2 неопределённости, но при этом все ситуации вполне корректны. Как представить результат в виде строки? Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 24, 2013, 12:04 Не знаю причем здесь строка. Ввести порог шума и сигнала - достаточно малые числа. типа 1.0e-8, это должно быть известно по задаче. Ну или просто if'ами. В любом случае exception здесь неуместны, т.к. ситуация штатная, корректная (по крайней мере с точки зрения пользователя), и обработке подлежит
Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 24, 2013, 12:25 Т.е. из 0 делаем 1.0e-8. При этом считаем в несколько этапов - вместо
Код пишем Код Боюсь, что это не пройдет code review из-за большого объёма и плохой читаемости, даже без учета логики (а если написать через if, то тем более). Пусть пришел сигнал 1e-8, а шум нулевой. Вместо 0 подставляем 1e-8. т.е. получается, что сигнал равен шуму - а это совсем неправильно. Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: m_ax от Сентябрь 24, 2013, 13:28 Да прибавьте в числителе и знаменателе (к шуму и сигналу) малую величину epsilon (cut-off)
Код
Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 24, 2013, 13:33 Код пишем Код Боюсь, что это не пройдет code review из-за большого объёма и плохой читаемости, Код
Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 24, 2013, 16:50 Ну вот добрались уже до 8 строчек вместо одной.
К тому же следует учесть, что все функции теоретически могут бросить исключение, и должны быть обернуты в try{}catch{}. Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 24, 2013, 18:05 Ну вот добрались уже до 8 строчек вместо одной. Не надо стремиться написать такКод: double sn_=s/n_; К тому же следует учесть, что все функции теоретически могут бросить исключение, и должны быть обернуты в try{}catch{}. Ни разу не видел чтобы напр sqrt, log. acos и.т.п. вызывали исключение. Дал некорректный аргумент - получил nan, вот и вся любовь. А если мы хотим идти своим путем, то проще бросить такКод
Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: m_ax от Сентябрь 24, 2013, 19:02 Цитировать Точно не пройдет, но никто не мешает записать это нормально, напр Ну это, конечно, нормально.. да) Банальная сдвижка сигнала и шума на маааленькую величину решает все эти проблемы) А чтоб не осталось сомнений в том, что эта маааленькая величина не вносит хоть какие-либо искажения в конечный результат привожу два графика этого логарифма при различных epsilon (0.001 и 0.0001). Даже при таких больших значениях epsilon, я не вижу разницы между графиками.. Код
Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 25, 2013, 08:07 Цитировать Банальная сдвижка сигнала и шума на маааленькую величину решает все эти проблемы) Код
А вдруг s или n оказались отрицательными? Они могли быть получены с помощью какой-то внешней функции, и кто знает какие там косяки. Цитировать Ни разу не видел чтобы напр sqrt, log. acos и.т.п. вызывали исключение. Кто их знает? Обычно не бросают, но может быть есть такие ОС, в которых бросают.Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 25, 2013, 10:27 Кто их знает? Обычно не бросают, но может быть есть такие ОС, в которых бросают. Кто = стандарт, от ОС не зависит.А вдруг s или n оказались отрицательными? Они могли быть получены с помощью какой-то внешней функции, и кто знает какие там косяки. Совершенно верно, поэтому я привел пример где Clamp загоняет их в валидный диапазон. Это часть работы, пусть не самая творческая, но которую надо делать, а не сачковатьНазвание: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: m_ax от Сентябрь 25, 2013, 10:45 А вдруг s или n оказались отрицательными? Они могли быть получены с помощью какой-то внешней функции, и кто знает какие там косяки. Код
Ну а если уж так хочется считать логарифм и от отрицательного аргумента, то комплексные числа никто не отменял (см. std::complex) Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: xokc от Сентябрь 26, 2013, 08:56 А вдруг s или n оказались отрицательными? Они могли быть получены с помощью какой-то внешней функции, и кто знает какие там косяки. А Вы сами что предполагаете рисовать на графике, если один из "косячных" случаев всё-же случился? Кто их знает? Обычно не бросают, но может быть есть такие ОС, в которых бросают. Ну с такой паранойей и i++ оборачивать try/catch нужно.Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 26, 2013, 10:39 Цитировать Ну с такой паранойей и i++ оборачивать try/catch нужно. А как же. Надо проверить, не будет ли переполнения. Т.е. что-то вроде Код Но это не кроссплатформенно, int не всегда 32 бита. Как это можно исправить? Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 26, 2013, 10:49 А Вы сами что предполагаете рисовать на графике, если один из "косячных" случаев всё-же случился? Ну, бесконечность можно изобразить вертикальной прямой. Отрицательные числа, когда они недопустимы, заменить на 0. Что делать с 0/0 вообще не знаю. Ваши варианты? Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: Igors от Сентябрь 26, 2013, 12:50 А как же. Надо проверить, не будет ли переполнения. Т.е. что-то вроде Код Но это не кроссплатформенно, int не всегда 32 бита. Как это можно исправить? Код Хотя мне неизвестна ни одна современная платформа где int не 4 байта. Вот long да, где 4 а где и 8. Вы так упорно стремитесь к исключениям как будто это хорошо/правильно и даже круто :) В действительности же ничего хорошего в этом нет. Ну выбросили исключение, дальше-то что? Сообщить пользователю "ошибка" - это не сделает его счастливым, тем более что ситуация вполне штатная Что делать с 0/0 вообще не знаю. Да как угодно, напр не рисовать вообще (только должна быть надпись "нет сигнала") или рисовать др цветом или пунктирной линией. Это вопрос дызайна.Ваши варианты? Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: zzzseregazzz от Сентябрь 26, 2013, 13:19 Сообщить пользователю "ошибка" - это не сделает его счастливым, тем более что ситуация вполне штатная Конечно не сделает. Но корректно обработать ошибку еще сложнее. А ошибка может произойти почти в каждой строке, даже i++. Получается, надо на каждую строку писать обработчик. В этом обработчике в свою очередь тоже могут возникнуть ошибки... И какой в итоге будет объём кода, где все возможные ошибки корректно обрабатываются? Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: mutineer от Сентябрь 26, 2013, 14:22 Сообщить пользователю "ошибка" - это не сделает его счастливым, тем более что ситуация вполне штатная Конечно не сделает. Но корректно обработать ошибку еще сложнее. А ошибка может произойти почти в каждой строке, даже i++. Получается, надо на каждую строку писать обработчик. В этом обработчике в свою очередь тоже могут возникнуть ошибки... И какой в итоге будет объём кода, где все возможные ошибки корректно обрабатываются? Если учитывать все возможные ошибки, даже в i++, то объём такого кода будет 0 строк. Потому что в условиях, когда нет уверенности даже в элементарных операциях, писать что-либо невозможно Название: Re: Как корректно обработать деление на 0, логарифм нуля, и т.п. Отправлено: xokc от Сентябрь 26, 2013, 14:30 Ну, бесконечность можно изобразить вертикальной прямой. Бесконечно вертикальной?Отрицательные числа, когда они недопустимы, заменить на 0. Так всё-таки допустимы отрицательные числа или нет? Если - нет, то делайте фильтр на входе типа s = qMax(1e-10, s) и n = qMax(1e-10, n) и тему можно закрывать. Если да - то Вы сначала сами для себя решите, чему равен log(-1), а потом мы Вам подскажем как это нарисовать.Вообще говоря, в обработке сигналов обычно имеет смысл говорить об отношении мощности сигнала к мощности шума. Эти величины заведомо неотрицательны, что сразу снимает все Ваши переживания. Что делать с 0/0 вообще не знаю. С учетом того, что решается задача всего лишь отображения графика, мой вариант (он неоднократно уже озвучен в этом топике) - прибавлять к сигналу и шуму по пренебрежимо малой величине. Вариант неоднократно опробирован при решении самых разных задач ЦОС, требующих гораздо большей точности, чем рисование графиков.Ваши варианты? |