Страницы

Поиск по вопросам

четверг, 30 мая 2019 г.

Безопасное деление на ноль

Как определить допустимое значение переменной типа float, используемой в качестве делителя, чтобы избежать ошибки деления на ноль? Достаточно ли сравнить с нулем?
float f; //... if (f != 0.f) 1.f / f;
Здесь похожий вопрос, где VladD ссылается на IEEE-754, где гарантируется:
Если v > 0 и h == 0, то v/h == INFINITY Если v < 0 и h == 0, то v/h == -INFINITY
Что в принципе позволит исключить проверку на ноль перед делением, но с другой стороны, стандарт 6.5.5.5 говорит:
The result of the / operator is the quotient from the division of the first operand by the second; the result of the % operator is the remainder. In both operations, if the value of the second operand is zero, the behavior is undefined
Вопрос в том, безопасно ли делить float с нулями, и если нет, какое должно быть условие проверки, чтобы получить определенный результат.


Ответ

В первую очередь хочу отметить, что для выяснения правил работы с числами с плавающей точкой в c или c++ нужно проверить значения соответствующих констант. Т.е. стандарты языков не гарантируют обязательности наличия этих правил, но имеется возможность узнать поддерживает ли та или иная реализация, например, стандарт IEEE-754 для определенного типа.
В приложении F (IEC 60559 floating-point arithmetic) стандарта языка c четко сказано:
An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex.
В c++ можно проверить поддержку IEEE-754 для вещественного типа с помощью такого кода:
std::numeric_limits::is_iec559
True if and only if the type adheres to IEC 559 standard. International Electrotechnical Commission standard 559 is the same as IEEE 754.
Если поддержка обеспечивается, то дальнейшее исследование вопроса нужно производить уже по упомянутому стандарту. Но как показывает практика, ошибка деления на ноль возникает исключительно при делении на ноль, хотя получить бесконечность можно и при делении на отличное от нуля значение:
#include #include #include
int main() { std::feclearexcept(FE_ALL_EXCEPT); float min = std::numeric_limits::min(); volatile float zero = 0.0f; std::cout << 10.f / min << '
'; if(std::fetestexcept(FE_DIVBYZERO)) { std::cout << "division by zero reported
"; } else { std::cout << "division by zero not reported
"; } }
Получим:
inf division by zero not reported
Если заменим min на zero, то уже
inf division by zero reported
Поэтому для себя надо решить, чего же действительно хочется избежать: ошибки деления на ноль или получения бесконечности.
Согласно IEEE-754 п.7.3 исключение типа "деление на ноль" возникает только при действиях на конечных операндах в двух случаях:
деление ненулевого значения на ноль; вычисление логарифма от нуля.

Комментариев нет:

Отправить комментарий