Все мы знаем, что к float
нельзя (точнее не рекомендуется) применять операцию ==
. Почему - думаю сами знаете. Но что насчет сравнения с 0? То есть, допустим, у нас есть какая-то переменная, проинициализированная нулем при создание, а далее, возможно, что ей будет присвоено какое-то значение, а может и не будет. Соответственно, если значение переменной равно нулю - то она не была проинициализирована. Так как при инициализации нулем все биты в переменной будут выставлены в 0, то я не вижу причин почему бы не сравнивать float
(в данном случае) с нулем. Так ли это?
Если известно, что число представлено точно, а именно:
то сравнивать на равенство можно.
В остальных случаях сравнение нежелательно и может привести к неправильному результату.
Не рекомендуется сравнивать вычисленный float на равенство с нулем.
Не рекомендуется сравнивать никакой float, ни вычисленный, ни константный на равенство с нулем (и на равенство двух float между собой).
Для константного float сравнение будет работать правильно, но это не общий случай.
Предположим есть код.
float n=0;
float a1;
float a2;
//здесь a1 и a2 получили какие-то значения,
//возможно пришедшие из внешнего мира (например считанные с АЦП)
float delta=a1-a2;
if(delta==n){
//do something1
}
else{
//do something2
}
Когда вычисляется разность delta a1 и a2 могут быть равны с точностью до точности типа float.
Но разность будет не равна нулю и соответственно, сравнение на равенство нулю не пройдет.
И алгоритм пойдет по другой ветке.
В качестве delta может быть не простая разность, а сколь угодно сложные вычисления.
В которых будут и ошибки округления, которые дадут +-1 к точности float.
Поэтому не рекомендуется.
Для двух констант, конечно, это будет работать.
Причем и для ненулевых констант тоже будет работать (на одной и той же платформе конечно).
Например:
float n1=0;
float n2=0;
if(n1==n2){
//do something1
}
else{
//do something2
}
Или пример для ненулевых констант:
float n1=1.1;
float n2=1.1;
if(n1==n2){
//do something1
}
else{
//do something2
}
Но в общем случае, когда float получен после вычислений, на конце у float будет +-1 точности float.
А может и больше, чем +-1 точности float.
UPD1:
И что такое "+-1 точности float" - это какой-то необщепринятый термин
Что-то я не помню, какой для этого есть общепринятый термин.
Может быть "машинный эпсилон" это термин для этого, то есть константа FLT_EPSILON из float.h.
А может быть FLT_MIN это термин для этого.
Наверное все-таки FLT_EPSILON.
UPD2:
Возможная подпрограмма сравнения на равенство для float (если сделать из нее шаблон, то подойдет и для других форматов с плавающей точкой):
bool compare_fl_point(float a1, float a2, float delta=FLT_EPSILON){
float delta1=a1-a2;
if(delta1<0) delta1=-delta1;
if(delta1>delta) return false; else return true;
}
В качестве параметра delta надо выбирать точность вычислений в данной задаче с учетом точности исходных данных и точности собственно вычислений на данной платформе.
Обычно рекомендуется, чтобы точность собственно вычислений на данной платформе превосходила точность входных данных как минимум на порядок.
UPD3:
EPSILON работает несколько иначе – Kromster 1 час назад
Эпсилон здесь только для примера в качестве дефолтного значения.
Более того, сравнивать с эпсилоном бессмысленно, так как эпсилон (по определению) всегда меньше или равен любому float числу.
На месте эпсилона должна быть точность входных данных в данной конкретной задаче.
Обычно точность вычислений выбирают так, чтобы она была как минимум в 10 раз выше, чем точность входных данных.
UPD4:
А вообще-то нужно погуглить этот вопрос.
Тут двух мнений быть не может, есть правила, написанные разработчиками стандарта на float.
Там однозначно говорится, как надо сравнивать float на равенство.
Я погуглил, но сходу выпало только такие же безответственные базары разработчиков на форумах, как и здесь.
Должен быть стандарт, в котором все это прописано.
Можно еще попробовать у Кнута посмотреть, может быть у него есть.
UPD5:
А вот, нашел что-то похожее на статью по теме:
https://habr.com/post/112953/
Там есть и про сравнение float на равенство.
И даже отсылка к какой-то импортной статье.
UPD6:
Решил провести вычислительный эксперимент, чтобы посмотреть, как появляются ошибки округления float.
Для этого написал программу, проверяющую известное тождество sin(x)^2+cos(x)^2=1;
Вот программа:
// Example program
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
const int numpoint=10;
int main(){
int i;
for(i=0;i!=numpoint;++i){
float arg=3.14/((float)i+1.0);
float s=sin(arg);
float c=cos(arg);
float one=s*s+c*c;
float delta=one-1.0;
bool flag;
if(delta==0.0) flag=true; else flag=false;
cout<<endl<<i<<". delta="<<delta<<" flag="<<flag<<" arg="<<arg;
}
return 0;
}
Результат работы программы:
0. delta=0 flag=1 arg=3.14
1. delta=0 flag=1 arg=1.57
2. delta=0 flag=1 arg=1.04667
3. delta=0 flag=1 arg=0.785
4. delta=0 flag=1 arg=0.628
5. delta=0 flag=1 arg=0.523333
6. delta=-5.96046e-08 flag=0 arg=0.448571
7. delta=-5.96046e-08 flag=0 arg=0.3925
8. delta=-5.96046e-08 flag=0 arg=0.348889
9. delta=-5.96046e-08 flag=0 arg=0.314
Видно, что при некоторых аргументах возникает delta не равная нулю и оператор сравнения delta с нулем
if(delta==0.0) flag=true; else flag=false;
выдает значение false.
Все это проверялось на http://cpp.sh/
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
OpenProcess не возвращает хэндл процесса, при этом hwnd и procID не равен NULL
Условие задачи на картинкеКод что я написал ниже: Почему-то всегда если ввожу большое число(около 1 * 10^10) выдает результат 17:174
Примитивная программа, при компилировании которой выходит две ошибки:
Пытаюсь создать exe файл через ClionНо при запуске exe файла, выдает ошибку о нехватке dll файла