Сравнение чисел с плавающей запятой

Сравнение чисел с плавающей запятой.

В этой теме я попробую "просто" объяснить новичкам в программировании что есть тип double (одинаково и для типа float), как эти типы вижу я и возможно не только я, сравнение чисел с плавающей запятой, что это такое и почему возникают сложности в этом процессе в программировании, хотя мы все в математике сравнивали дробные числа на бумаге и это не вызывало сложностей (по крайней мере не у всех, и не такого уровня, как при написании программ, где необходимо сравнивать такие числа).

Для начинающего программиста знакомство с языком и его синтаксисом, начинается с изучения таких понятий как типы данных. Все мы знаем 6 стандартных типов данных (int, char, wchar_t, bool, float и double), он не все знают, что это такое и из чего эти типы состоят и как работает их логика. Рассматривать мы будем только тип double, так как с ним наиболее часто возникают сложности у новичков. Сам я столкнулся с этой проблемой, когда писал программу с математическим уклоном (просто для себя), и все пошло не так в момент, когда надо было сравнивать числа типа double.

Небольшой пример, в котором многие найдут и свой личный пример:

Например, мы имеем 4 переменных типа double (double number1, double number2, double result, double UserResult). Две (double number1, double number2) используются для хранения значений, с которыми мы будем работать, 1 для хранения результата операций с этими значениями (double result), вычисленные процессором, и 1 переменная, в которую пользователь будет вводить с клавиатуры свой результат вычисления этого же примера(double UserResult).

Итак мы имеем:

C++Выделить код

1
2
3
4
5
6
7
8
9
10
11

result = number1 / number2;
cout << “Введите свой результат” << endl;
cin >> UserResult;
if (result == UserResult)
{
cout << “Ответ верный” << endl;
}
else
{
cout << “Ответ неверный” << endl;
};

Любые попытки ввести и сравнить числа приведут к появлению сообщения “Ответ неверный”, потому что символы в массивах не будут совпадать, и сравнение никогда не будет true.

Тип double представляет пользователю число, записанное в символьный массив (const char*), именно этим и объясняется возможность хранения таких символов как '.' - разделяющий целую часть от дробной, так же '^' - означающий экспоненту (exponent - показатель степени) в которую надо возвести мантиссу, '-' - знак минуса, который показывает что степень отрицательная, 'е' – символ, который используется иногда для разграничения мантиссы и экспоненты (означает основание степени, в 99,9% случаев = 10). Сколько бы мы не пытались записать символ '.' или любой другой из вышеперечисленных в любой тип данных (кроме char и string и естественно float и double), это будет невозможно, так как он не является числом и никогда им являться не будет, однако тип double призван работать с числами, и в тоже время он принимает и символы, что дает основание почти 100% утверждать что, запись числа идет в статический символьный массив, размер которого ограничен количеством цифр, в типе и что конечная форма предоставления числа с плавающей запятой тоже является символьным массивом, и содержит символы цифр, а не значения этих цифр, именно поэтому сравниваются эти числа посимвольно, а не по значению.

Это и объясняет, почему при вычислении числа, которое записано в виде символьного массива (const char*) форме мантисса и экспонента, применяется схема превращения числа в форму, пригодную для выполнения математических операций над ней (скорее всего числа представляются в виде обыкновенных дробей, или в форме x/y, так как это удобная форма деления простых целых чисел, конечно о действительном механизме этого процесса можно гадать много, так как способов его реализации тоже много). Все дело в том, что сложить число 1.2345 и 1.2345, представленные в виде символьных массивов просто невозможно, так как процессор воспринимает эту форму не как число (чем отличаются такие понятия как «цифры» и «числа», вы естественно знаете), а как цифры – знаки, символы, и он не может выполнять с ними математические манипуляции.

К примеру, мы пытаемся сравнить число, отвечающее за время, и берем такие значения как 1.00 и 13.00. Для нас это одно и тоже, только 1.00 это 12-ти часовой тип представления времени, а 13.00 это 24-х часовой тип представления времени, но от этого значение времени не меняется. Час дня, есть час дня, что 1.00, что 13.00. Но если мы попытаемся сравнить эти числа в виде символьного массива, то эти массивы не будут одинаковые, потому что символы '1','.','0','0' и символы '1','3','.','0','0' различаются по составу символов, они не похожи. Тоже самое происходит, когда число 1.20*10^4, 1.20е4 и тоже самое число 1.20000 хранимое в переменной типа double (а мы знаем, что не все числа представлены в виде мантисса и экспонента «1.20*10^4», некоторые имеют форму представления «1.20е4» и «1.200000»), в разных формах, не будут одинаковыми при сравнении их между собой (1.20*10^4 != 1.20е4 && 1.20*10^4 != 1.200000 && 1.20е4 != 1.200000) потому что они различается символами, которыми представлено на обозрение пользователю, хотя мы прекрасно понимаем, что значение этого числа одинаковое, что в первой форме, что во второй, что в третьей.

Надеюсь понимание того что описано выше, поможет многим новичкам решить проблему сравнения чисел с плавающей запятой в своих конкретных случаях.

В случае вопросов по теме, выделите цитатой интересующий момент, если не знаю ответ сам, то будем думать вместе….

Дополнения и критика приветствуются.