Ответ 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).