Чтобы добиться этого, надо написать собственные функции сравнения чисел с двойной точностью:
doubleLess
,
doubleEquals
и
doubleGreater
, каждая из которых принимает в качестве параметров два значения типа
double
. Кроме того,
doubleLess
и
doubleGreater
имеют дополнительный параметр, который при его равенстве
true
приводит к тому, что эти функции ведут себя как «меньше или равно» и «больше или равно» соответственно.
Чтобы
заставить эти функции учитывать точность, рассмотрим функцию
doubleEquals
. Вместо того чтобы проверять на равенство, эта функция проверяет, находится ли разность двух чисел в указанном пользователем диапазоне
epsilon
. (В качестве
epsilon
пример использует значение 0.0001.) Если это так, то функция возвращает значение true, что означает, что значения одинаковы. Таким образом, равными окажутся значения 0.3333, 0.33333, 0.333333, 0.33333333333 и 0.33333323438.
Чтобы выполнить операцию «меньше чем» и «больше чем», вначале проверьте, не равны ли значения, как это делается в функции
doubleEquals
. Если так, то при наличии теста на равенство верните
true
, а в противном случае —
false
. В противном случае выполните прямое сравнение.
3.5. Лексический анализ строки, содержащей число в экспоненциальной форме
Проблема
Имеется строка, содержащая число в экспоненциальной форме, и требуется сохранить значение числа в переменной типа
double
.
Решение
Наиболее простым способом анализа числа в экспоненциальной форме является использование встроенного в библиотеку C++ класса
stringstream
, объявленного в
<sstream>
, как показано в примере 3.7.
Пример 3.7. Лексический анализ числа в экспоненциальной форме
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
double sciToDub(const strings str) {
stringstream ss(str);
double d = 0;
ss >> d;
if (ss.fail) {
string s = "Невозможно отформатировать ";
s += str;
s += " как число!";
throw (s);
}
return (d);
}
int main {
try {
cout << sciToDub("1.234e5") << endl;
cout << sciToDub("6.02e-2") << endl;
cout << sciToDub("asdf") << endl;
} catch (string& e) {
cerr << "Ошибка: " << e << endl;
}
}
Далее
показан вывод этого кода.
123400
0.0602
Ошибка: невозможно отформатировать asd как число!
Обсуждение
Класс
stringstream
— это
string
, который ведет себя как поток (что неудивительно). Он объявлен в
<sstring>
. Если требуется выполнить анализ
string
, содержащей число в экспоненциальной форме (см. также рецепт 3.2), то с этой работой прекрасно справится
stringstream
. Стандартные классы потоков уже «знают», как анализировать числа, так что не тратьте без острой необходимости время на повторную реализацию этой логики.
В примере 3.7 я написал простую функцию
sciToDub
, принимающую параметр типа
string
и возвращающую содержащийся в ней
double
, если он допустим. В
sciToDub
я использую
stringstream
следующим образом.
stringstream ss(str); // Конструирование из строки типа string
double d = 0;
ss >> d;
if (ss.fail) {
string s = "Невозможно отформатировать ";
s += str;
s += " как число!";
throw (s);
}
return (d);
Наиболее важной частью здесь является то, что все, что требуется сделать, — это использовать для чтения из строкового потока в
double
оператор сдвига вправо (
>>
), как это делается при чтении из
cin
.
Ну, это не совсем все, что требуется сделать. Если в
stringstream
записано значение, которое не может быть записано в переменную в правой части оператора
>>
, то для потока будет выставлен бит
fail
. Этот бит можно проверить с помощью функции-члена
fail
(на самом деле это функция-член
basic_ios
, который является родительским классом для
stringstream
). Кроме того, переменная справа от оператора
>>
в случае ошибки значения не меняет.
Однако с целью обобщения можно избежать написания отдельных версий
sciToDub
для типов
int
,
float
,
double
и чего-либо еще, что может потребоваться преобразовать, если написать шаблон функции. Рассмотрим такую новую версию.