Подтвердить что ты не робот

Плавающая точка - это сравнение равенства, достаточное для предотвращения деления на ноль?

// value will always be in the range of [0.0 - maximum]

float obtainRatio(float value, float maximum){
    if(maximum != 0.f){
        return value / maximum;  
    }else{
        return 0.f;
    }
}

Диапазон maximum может быть любым, включая отрицательные числа. Диапазон value также может быть чем угодно, хотя функция требуется только для "смысла", когда вход находится в диапазоне [0.0 - maximum]. Выход всегда должен находиться в диапазоне [0.0 - 1.0]

У меня есть два вопроса, о которых мне интересно, с этим:

  • Является ли это сравнение равенства достаточным для того, чтобы функция никогда не делялась на нуль?
  • Если максимум является вырожденным значением (чрезвычайно малым или очень большим), есть ли вероятность, что функция вернет результат вне [0.0 - 1.0] (если значение находится в правильном диапазоне)?
4b9b3361

Ответ 1

Вот поздний ответ, поясняющий некоторые понятия по отношению к вопросу:

Просто верните значение/максимум

В плавающей точке деление на ноль не является фатальной ошибкой, как целочисленное деление на ноль. Поскольку вы знаете, что value находится между 0.0 и maximum, единственным делением на ноль, которое может произойти, является 0.0 / 0.0, которое определяется как создание NaN. Значение с плавающей запятой NaN является вполне приемлемым значением для функции obtainRatio для возврата и на самом деле является гораздо лучшим исключительным значением для возврата, чем 0.0, по мере того, как возвращается ваша предлагаемая версия.

Суеверия о плавающей запятой - это только суеверия

Нет ничего приблизительного в определении <= между поплавками. a <= b иногда не оценивает значение true, когда a немного выше b. Если a и b являются двумя конечными переменными float, a <= b оценивается как истинное, когда рациональное представление a меньше или равно рациональному, представленному b. Единственный маленький глюк, который можно воспринимать, на самом деле не является глюком, а строгой интерпретацией вышеприведенного правила: +0.0 <= -0.0 оценивается как истина, потому что "рациональное представление, представленное +0.0" и "рациональное представление, представленное -0.0", оба 0.

Точно так же нет ничего около == между float: две конечные переменные float a и b делают a == b true тогда и только тогда, когда рациональное представление a, а рациональное представление b одинаково.

В рамках условия if (f != 0.0) значение f не может быть представлением нуля, и поэтому деление на f не может быть делением на ноль. Разделение все еще может переполняться. В частном случае value / maximum не может быть переполнения, потому что для вашей функции требуется 0 ≤ value ≤ maximum. И нам не нужно задаваться вопросом, означает ли в предварительном условии отношение между рациональностями или отношением между поплавками, поскольку эти два по существу одинаковы.

Это сказанное

C99 допускает дополнительную точность для выражений с плавающей запятой, которая ранее использовалась , неправильно интерпретируемой производителями компилятора как лицензия на непредсказуемое поведение с плавающей запятой (до такой степени, что программа if (m != 0.) { if (m == 0.) printf("oh"); } можно ожидать, что напечатает "о" в некоторых случаях).

В действительности, компилятор C99, предлагающий с плавающей запятой IEEE 754 и определяющий FLT_EVAL_METHOD к неотрицательному значению, не может изменить значение m после того, как он был протестирован. Переменной m было присвоено значение, представляемое как float, когда оно было последним назначено, и это значение является либо представлением 0, либо нет. Только операции и константы могут иметь избыточную точность (см. Стандарт C99, 5.2.4.2.2: 8).

В случае GCC последние версии делают то, что является правильным с -fexcess-precision=standard, подразумеваемым -std=c99.

Дальнейшее чтение

  • David Monniaux описание печального состояния плавающей запятой в C несколько лет назад (первая версия опубликована в 2007 году). Отчет Дэвида не пытается интерпретировать стандарт C99, но описывает реальность вычисления с плавающей запятой в C, как это было тогда, с реальными примерами. Ситуация значительно улучшилась, поскольку благодаря улучшенному стандарту соответствия в компиляторах, которые заботятся и благодаря набору инструкций SSE2, что делает всю проблему проблемой.

  • почтовая рассылка 2008 года Джозефа С. Майерса, описывающая тогдашнюю ситуацию GCC с поплавками в GCC (плохой), как он интерпретировал стандарт (хороший) и как он реализовал свою интерпретацию в GCC (GOOD).

Ответ 2

В этом случае с ограниченным диапазоном это должно быть ОК. В общем случае проверка нуля сначала предотвратит деление на ноль, но есть вероятность получить переполнение, если делитель близок к нулю, а дивиденд - большое число, но в этом случае дивиденд будет мал, если делитель мал (оба могут быть близки к нулю, не вызывая переполнения).