Предотвращение деления на ноль: проверка выражения делителя не приводит к нулю, а проверка делителя не равна нулю?

Возможно ли деление на ноль в следующем случае из-за ошибки с плавающей точкой в ​​вычитании?

float x, y, z;
...
if (y != 1.0)
z = x / (y - 1.0);

Другими словами, безопаснее ли следующее?

float divisor = y - 1.0;
if (divisor != 0.0)
z = x / divisor;

7

Решение

Это предотвратит деление на ноль, но это не значит, что все равно не получится +/-inf в следствии. Знаменатель все еще может быть достаточно маленьким, так что ответ не может быть представлен double и вы получите inf, Например:

#include <iostream>
#include <limits>

int main(int argc, char const *argv[])
{
double small = std::numeric_limits<double>::epsilon();
double large = std::numeric_limits<double>::max() / small;
std::cout << "small: " << small << std::endl;
std::cout << "large: " << large << std::endl;
return 0;
}

В этой программе small ненулевой, но он настолько мал, что large превышает диапазон double и является inf,

5

Другие решения

Предполагая IEEE-754 с плавающей точкой, они эквивалентны.

Основная теорема арифметики FP состоит в том, что для конечных x и y x — y == 0 тогда и только тогда, когда x == y, при условии постепенного уменьшения значения.

Если субнормальные результаты сбрасываются в ноль (вместо постепенного понижения), эта теорема справедлива, только если результат x — y является нормальным. Потому что 1.0 хорошо масштабируется, y - 1.0 никогда не бывает ненормальным, и так y - 1.0 равен нулю тогда и только тогда, когда у равен ровно 1,0, независимо от того, как обрабатывается потеря значения.

Конечно, C ++ не гарантирует IEEE-754, но эта теорема верна для большинства «разумных» систем с плавающей точкой.

6

Нет никакой разницы между двумя фрагментами кода () — фактически, оптимизатор может даже оптимизировать оба фрагмента в один и тот же двоичный код, предполагая, что дальнейшее использование divisor переменная.

Обратите внимание, однако, что деление на ноль с плавающей точкой 0.0 не приводит к ошибке во время выполнения, но выдает inf или же -inf вместо.

2
По вопросам рекламы [email protected]