Даже если вы не занимаетесь написанием научных или инженерных приложений, вам все равно придется работать с числами. Эта глава содержит решения проблем, часто возникающих при работе с числовыми типами С++.
Некоторые из рецептов содержат методики преобразования из числовых типов в тип
string и обратно чисел, представленных в различных форматах (шестнадцатеричном, с плавающей точкой или экспоненциальном). Самостоятельное написание кода для таких преобразований утомительно и требует времени, так что я показываю возможности стандартной библиотеки или одной из библиотек Boost, облегчающие выполнение этих задач. Также имеется несколько рецептов по работе исключительно с числовыми типами: безопасное преобразование между ними, сравнение чисел с плавающей точкой с граничными значениями и поиск минимального и максимального значений.
Рецепты в этой главе предоставляют решения некоторых общих проблем, с которыми обычно сталкиваются при работе с числами в С++, но они не пытаются решать проблем, специфичных для конкретных приложений. При написании научного или инженерного приложения вам также следует взглянуть на главу 11, которая содержит рецепты ко многим общим научным и инженерным алгоритмам.
Имеются числа в строковом формате, и вам требуется преобразовать их в числовой тип, такой как
int или float.
Это можно сделать двумя способами — с помощью функций стандартной библиотеки или с помощью класса
lexical_cast из Boost (написанного Кевлином Хенни (Kevlin Henney) Функции стандартной библиотеки неуклюжи и небезопасны, но они стандартны, и в некоторых случаях потребуются именно они, так что в первом решении я представлю именно их. lexical_cast более безопасен, проще в использовании и интереснее, так что я представляю его в обсуждении.
Функции
strtol, strtod и strtoul, определенные в , преобразуют символьные строки, ограниченные нулем, в long int, double или unsigned long. Они могут использоваться для преобразования чисел, представленных в виде строк с любым основанием, в числовые типы. Код примера 3.1 демонстрирует функцию hex2int, которая предназначена для преобразования шестнадцатиричной строки в long.
Пример 3.1. Преобразование числовых строк в числа
#include
#include
#include
using namespace std;
long hex2int(const string& hexStr) {
char *offset;
if (hexStr.length( ) > 2) {
if (hexStr[0] == '0' && hexStr[1] == 'x') {
return strtol(hexStr.c_str(), &offset, 0);
}
}
return strtol(hexStr.c_str( ), &offset, 16);
}
int main() {
string str1 = "0x12AB";
cout << hex2int(str1) << endl;
string str2 = "12AB";
cout << hex2int(str2) << endl;
string str3 = "0AFG";
cout << hex2int(str3) << endl;
}
Вот вывод этой программы.
4779
4779
0
Первые две строки содержат шестнадцатеричное число 12AB. Первая из них содержит префикс
0x, а вторая — нет. Третья строка не содержит правильного шестнадцатеричного числа. В этом случае функция просто возвращает 0.
Некоторые люди склонны писать свои собственные функции для преобразования шестнадцатеричных чисел в целочисленные форматы. Но зачем изобретать колесо? Стандартная библиотека уже предоставляет эту функциональность. Пример 3.1 представляет собой функцию-оболочку, упрощающую вызов
strtol. Функция strtol — это старая функция библиотеки С, и она требует от вас передачи указателя на завершающуюся нулем строку, а так же адрес еще одного указателя на строку. Этот второй указатель получает адрес, на котором обработка строки завершилась. Однако в C++ большинство людей предпочитает работать с более мощным классом string, а не со старыми указателями на символьные строки. Поэтому функция hex2int принимает параметр типа string.
Функция
strtol несколько странна в том, что она позволяет использовать два разных метода указания основания 16: 16 можно передать как третий параметр функции, а можно в качестве основания передать 0, но предварить строку символами 0x (точно также, как это делается для обозначения шестнадцатеричных чисел в коде, но только помните, что в случае с strtol передается строка).
Пример 3.1 позволяет использовать оба метода. При передаче строки вида
0x12AB функция обнаружит 0x и передаст ее непосредственно в strtol, в качестве третьего параметра передав 0. В противном случае функция передаст строку, в качестве третьего параметра передав 16.
strtol и strtoul работают одинаково, за исключением типа возвращаемого значения. strtod аналогична им, но не позволяет указывать основание.
Эти старые функции С не являются единственным способом преобразования строк в числа. Проект Boost предоставляет класс преобразования
lexical_cast, который выполняет то же самое для числовых строк, записанных с основанием 10. Пример 3.2 показывает как он используется.
Пример 3.2. Использование lexical_cast
#include
#include
#include
using namespace std;
int main() {
string str1 = "750" ;
string str2 = "2.71";
string str3 = "0x7FFF";
try {
cout << boost::lexical_cast(str1) << endl;
cout << boost::lexical_cast(str2) << endl;
cout << boost::lexical_cast(str3) << endl;
} catch (boost::bad_lexical_cast& e) {
cerr << "Bad cast: " << e.what() << endl;
}
}
Вывод примера 3.2 таков.
750
2.71
Bad cast: bad lexical cast: source type value could not be
interpreted as target
(Неверное преобразование: неверное лексическое преобразование: значение исходного типа не может быть преобразовано в целевой.)
Вы ведите, что для последнего значения, представляющего собой шестнадцатеричное число, он выбрасывает исключение. При преобразовании чисел с основанием, отличным от 10, требуется использовать функции
strtol.
Также имеются версии функций
strtol для работы с «широкими» символами. Эквивалент strtol для работы с широкими символами — это wcstol, которая объявлена в . Эквивалентами функций strtod и strtoul являются wcstod и wcstoul. Каждая из этих функций точно такая же, за исключением того, что те параметры, которые в функциях для узких символов имеют тип char*, в функциях для широких символов имеют тип wchar_t*.
Рецепт 3.2.
Имеются числовые типы (
int, float), и вам требуется поместить их содержимое в string, возможно, предварительно отформатировав.
Для выполнения этого имеется множество способов, каждый из которых имеет свои достоинства и недостатки. Первая представляемая мной методика использует для хранения строковых данных класс
stringstream, который является частью стандартной библиотеки и прост в использовании. Этот подход показан в примере 3.3. Смотри обсуждение альтернативных методик.
Пример 3.3. Форматирование числа как строки
#include
#include
#include
#include
using namespace std;
int main() {
stringstream ss;
ss << "В моей корзине " << 9 << " яблок.";
cout<
// с содержимым
ss.str(""); // Очистка строки
ss << showbase << hex << 16; // Показать основание в шестнадцатеричном формате
cout << "ss = " << ss.str() << endl;
ss.str("");
ss << 3.14;
cout << "ss = " << ss.str() << endl;
}
Вывод примера 3.3 выглядит так.
В моей корзине 9 яблок.
ss = 0x10
ss = 3.14
stringstream — это удобный способ поместить данные в string, поскольку он позволяет использовать все возможности форматирования, предоставляемые классами стандартного ввода и вывода. В простейшем случае в примере 3.3 я для записи комбинации текста и числовых данных в строковый поток просто использую оператор сдвига влево (<<).
ss << "В моей корзине " << 9 << " яблок.";
Оператор
<< перегружен для встроенных типов и соответственно форматирует вывод. Когда требуется получить данные, хранящиеся в string, используйте функцию-член str.
cout << ss.str() << endl;
В
имеется большое количество манипуляторов потоками, и их использование при выводе числовых данных в строку позволяет выполнить все виды форматирования. В примере 3.3 для форматирования числа как шестнадцатеричного я использовал showbase и hex, но есть еще и другие возможности форматирования. Например, можно установить точность отображения, отличную от числа десятичных знаков по умолчанию.
ss << setprecision(6) << 3.14285;
Однако использование манипуляторов является не самой интуитивно понятной вещью, и именно поэтому создан рецепт, посвященный им. За дополнительной информацией о форматировании числовых данных с помощью манипуляторов потоками обратитесь к рецепту 10.2.
Конечно, как часто бывает в С++, имеется и другой способ. Библиотека Boost Format (написанная Сэмюэлем Кремппом (Samuel Krempp) содержит класс
format, который делает форматирование и преобразование очень простыми. Пример 3.4 показывает, как выполнить подобное преобразование.
Пример 3.4. Форматирование целых в шестнадцатеричное представление
#include
#include
using namespace std;
using boost::format;
using boost.:io::str;
using boost::io::format_error;
int main() {
try {
format f("Имеется %1% способа. %2% %3% %4%");
f % 3;
f % "чтобы" % "это" % "сделать.";
cout << f << endl;
f.clear(); // Счистка буферов для форматирования чего-либо еще
f.parse("Это стоит $%d.");
f % 50;
cout << f << endl;
int x = 11256099;
string strx = str(format("%x") % x);
cout << strx << endl;
} catch (format_error &e) {
cout << e.what() << endl;
}
}
Вот что вы увидите при запуске этой программы.
Имеется 3 способа, чтобы это сделать.
Это стоит $50.
abc123
Использование класса
format требует двух шагов, включая создание объекта format и передачу ему содержимого. Для простейшего случая в примере 3.4 я создал объект format с помощью простейшей версии его синтаксиса.
format f(" Имеется %1% способа, %2% %3% %4%");
В строке формата заполнители — это числа, обрамленные с обеих сторон символами %. Затем я начинаю передавать в объект содержимое указанного формата.
f % 3;
f % "чтобы" % "это" % "сделать;
Оператор
% в библиотеке форматирования был переопределен так, чтобы добавлять указанные в нем переменные в левую часть объекта format. Его можно использовать как один раз на строку, так и несколько раз в одной строке. Он аналогичен оператору << для строк. Что же касается оператора <<, он также был переопределен так, что объекты format можно непосредственно записать в выходной поток. Кроме того, если требуется поместить результаты в строку, используйте функцию-член str.
string s = f.str();
Если же вам нравится
printf, то можно использовать форматную строку printf.
f.parse("Это стоит $%d.*");
f % 50;
Если будет записано слишком много или слишком мало переменных для указанного формата, то при попытке записать строку в поток или извлечь отформатированную строку будет выброшено исключение
format_error (или подкласс thereof).
Класс
format достаточно мощен и содержит слишком много возможностей форматирования, чтобы их можно было описать здесь, и его стоит изучить. Чтобы скачать Boost или почитать документацию, посетите web-сайт Boost по адресу www.boost.org.
Также для преобразования чисел из числовых типов в строки можно использовать
sprintf или аналогичные ей функции. Обычно этого следует избегать, так как это небезопасно и для этого имеются лучшие альтернативы.
Глава 10.
Имеется строка
string и требуется определить, содержит ли она допустимое число.
Для проверки допустимости числа можно использовать шаблон функции
lexical_cast библиотеки Boost. При таком подходе допустимое число может включать предшествующий знак минус, предшествующий знак плюс, но не пробел. В примере 3.5 приводятся несколько образцов типов форматов, с которыми работает lexical_cast.
Пример 3.5. Проверка числовой строки
#include
#include
using namespace std;
using boost::lexical_cast;
using boost::bad_lexical_cast;
template
bool isValid(const string& num) {
bool res = true;
try {
T tmp = lexical_cast(num);
} catch (bad_lexical_cast &e) {
res = false;
}
return(res);
}
void test(const string& s) {
if (isValid(s))
cout << s << " - допустимое целое число." << endl;
else
cout << s << " - HE допустимое целое число." << endl;
if (isValid(s))
cout << s << " - допустимое число двойной точности." << endl;
else
cout << s << " - HE допустимое число двойной точности." << endl;
if (isValid(s))
cout << s << " - допустимое число одинарной точности." << endl;
else
cout << s << " - HE допустимое число одинарной точности " << endl;
}
int main() {
test("12345");
test("1.23456");
test("-1.23456");
test(" - 1.23456");
test("+1.23456");
test(" 1.23456 ");
test("asdf");
}
Вот вывод этого примера.
12345 - допустимое целое число.
12345 - допустимое число двойной точности.
12345 - допустимое число одинарной точности.
1.23456 - НЕ допустимое целое число.
1.23456 - допустимое число двойной точности.
1.23456 - допустимое число одинарной точности.
-1.23456 - НЕ допустимое целое число.
-1.23456 - допустимое число двойной точности.
-1.23456 - допустимое число одинарной точности.
- 1.23456 - НЕ допустимое целое число.
- 1 23466 - НЕ допустимое число двойной точности.
- 1.23456 - НЕ допустимое число одинарной точности.
+1.23456 - НЕ допустимое целое число.
+1.23456 - допустимое число двойной точности.
+1.23456 - допустимое число одинарной точности.
1.23456 - НЕ допустимое целое число.
1.23456 - НЕ допустимое число двойной точности.
1.23456 - НЕ допустимое число одинарной точности.
asdf - НЕ допустимое целое число.
asdf - НЕ допустимое число двойной точности.
asdf - НЕ допустимое число одинарной точности.
Шаблон функции
lexical_cast преобразует значение из одного типа в другой. Он объявлен следующим образом.
template
Target lexical_cast(Source arg)
Source — это тип оригинальной переменной, a Target — это тип переменной, в которую значение преобразуется. Таким образом, например, чтобы преобразовать из string в int, вызов lexical_cast имеет вид:
int i = lexical_cast(str); // str - это строка
lexical_cast проводит анализ и пытается выполнить преобразование. Если преобразование невозможно, он выбрасывает исключение bad_lexical_cast. В примере 3.5 я только хочу проверить допустимость, и мне не требуется сохранять целевую переменную, так что если исключение не выбрасывается, я возвращаю true, а в противном случае — false.
В
lexical_cast требуется передать только первый аргумент, поскольку это шаблон, что означает, что компилятор может догадаться, какой тип имеет аргумент функции, и использовать его в качестве второго аргумента. Пояснение этой ситуации более сложно, чем простая демонстрация, так что позвольте мне использовать фрагмент кода примера. Вместо того чтобы вызывать lexical_cast, как в предыдущем фрагменте кода, можно сделать так.
int i = lexical_cast(str);
Это означает то же самое, но указывать аргумент
string не требуется, так как компилятор видит, что str — это string, и понимает, что от него требуется дальше.
Если вы собираетесь написать аналогичную функцию-обертку для проверки допустимости, возвращающую
true и false, ее также можно написать как шаблон функции. В этом случае ее потребуется написать только один раз с использованием параметризованного типа, а различные версии будут генерироваться при каждом ее использовании с различными типами.
lexical_cast также удобен для преобразования из одного числового типа в другой. Более подробно это обсуждается в рецепте 3.6.
Рецепт 3.6.
Требуется сравнить значения с плавающей точкой, но при этом выполнить сравнение на равенство, больше чем или меньше чем с ограниченным количеством десятичных знаков. Например, требуется, чтобы 3.33333 и 3.33333333 считались при сравнении с точностью 0.0001 равными.
Напишите свои функции сравнения, которые принимают в качестве параметра ограничение точности сравнения. Пример 3.6 показывает основную методику, используемую в такой функции сравнения.
Пример 3.6. Сравнение чисел с плавающей точкой
#include
#include // для fabs()
using namespace std;
bool doubleEquals(double left, double right, double epsilon) {
return (fabs(left - right) < epsilon);
}
bool doubleLess(double left, double right, double epsilon,
bool orequal = false) {
if (fabs(left - right) < epsilon) {
// В рамках epsilon, так что считаются равными
return (orequal);
}
return (left < right);
}
bool doubleGreater(double left, double right, double epsilon,
bool orequal = false) {
if (fabs(left - right) < epsilon) {
// В рамках epsilon, так что считаются равными
return (orequal);
}
return (left > right);
}
int main() {
double first = 0.33333333;
double second = 1.0 / 3.0;
cout << first << endl;
cout << second << endl;
// Тест на прямое равенство. Не проходит тогда, когда должно проходить.
// (boolalpha печатает булевы значения как "true" или "false")
cout << boolalpha << (first == second) << endl;
// Новое равенство. Проходит так, как требуется в научном приложении.
cout << doubleEquals(first, second, .0001) << endl;
// Новое меньше чем
cout << doubleLess(first, second, .0001) << endl;
// Новое больше чем
cout << doubleGreater(first, second, .0001) << endl;
// Новое меньше чем или равно
cout << doubleLess(first, second, .0001, true) << endl;
// Новое больше чем или равно
cout << doubleGreater(first, second, .0001, true) << endl;
}
Далее показан вывод этого примера.
0.333333
0.333333
false
true
false
false
true
true
Код примера 3.6 начинается с двух значений — 0.33333333 и того, что компьютер получает в результате деления 1.0 / 3.0. Он с помощью форматирования по умолчанию
cout печатает эти два значения. Они кажутся одинаковыми и равными 0.333333. Однако при сравнении этих двух значений они оказываются различными. Значение 1.0 / 3.0 имеет больше значащих цифр, чем 0.33333333, и, следовательно, как полагает машина, эти два числа не равны. Однако в некоторых приложениях может потребоваться, чтобы они считались равными.
Чтобы добиться этого, надо написать собственные функции сравнения чисел с двойной точностью:
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. В противном случае выполните прямое сравнение.
Имеется строка, содержащая число в экспоненциальной форме, и требуется сохранить значение числа в переменной типа
double.
Наиболее простым способом анализа числа в экспоненциальной форме является использование встроенного в библиотеку C++ класса
stringstream, объявленного в , как показано в примере 3.7.
Пример 3.7. Лексический анализ числа в экспоненциальной форме
#include
#include
#include
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, который ведет себя как поток (что неудивительно). Он объявлен в . Если требуется выполнить анализ 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 и чего-либо еще, что может потребоваться преобразовать, если написать шаблон функции. Рассмотрим такую новую версию.
template
T strToNum(const string& str) {
stringstream ss(str);
T tmp;
ss >> tmp;
if (ss.fail()) {
string s = "Невозможно отформатировать ";
s += str;
s += " как число!";
throw (s);
}
return (tmp);
}
Теперь, чтобы преобразовать
string в числовой тип, можно сделать так.
double d = strToNum("7.0");
float f = strToNum("7.0");
int i = strToNum("7.0");
Также параметром шаблона можно сделать тип символов, но это очень просто сделать, так что я оставляю это в качестве вашего упражнения.
Рецепт 3.2.
Имеется число одного типа и требуется преобразовать его в другой, как
int в short или наоборот, но при этом необходимо перехватывать все ошибки переполнения (overflow) или потери значимости (underflow), возникающие при работе программы.
Используйте шаблон класса
numeric_cast Boost. Он выполняет проверки, которые при переполнениях переменной, принимающей значение, или других ошибках выбрасывают исключение типа bad_numeric_cast. Пример 3.8 показывает, как это выполняется.
Пример 3.8. Безопасное преобразование чисел
#include
#include
using namespace std;
using boost::numeric_cast;
using boost::bad_numeric_cast;
int main() {
// Целые типы
try {
int i = 32767;
short s = numeric_cast(i);
cout << "s = " << s << endl;
i++; // Теперь i выходит за диапазон (если sizeof(short) равен 2)
s = numeric__cast(i);
} catch (bad_numeric_cast& e) {
cerr << e.what() << endl;
}
try {
int i = 300;
unsigned int ui = numeric_cast(i);
cout << ui << endl; // Прекрасно
i *= -1;
ui = numeric_cast(i); // i отрицателен!
} catch (bad_numeric_cast& e) {
cerr << e.what() << endl;
}
try {
double d = 3.14.
int i = numeric_cast(d);
i = numeric_cast(d); // Это отрезает 0.14!
cout << i << endl; // i = 3
} catch (bad_numeric_cast& e) {
cerr << e.what( ) << endl;
}
}
Вы, вероятно, знаете, что базовые типы C++ имеют различные размеры. Стандарт C++ содержит жесткие указания по относительному размеру типов:
int всегда не короче, чем short int, но он не указывает абсолютных размеров. Это означает, что если взять long int и попытаться записать его значение в short или попытаться поместить int в unsigned int, то информация о значении переменной-источника, такая как знак или даже часть числового значения, может быть потеряна.
Только знания, что это может привести к проблемам, не достаточно. Вы можете быть ограничены жесткими требованиями по объему и не захотите использовать четыре байта для
long, когда можно обойтись двумя байтами для short (если ваша платформа на самом деле использует такие размеры, что очень распространено, но не гарантируется). Из-за ограничений по объему может возникнуть желание попробовать хранить значения в наименьших возможных типах. Если вы любите приключения, но вам нужна страховка, для перехвата потерь данных при работе программы используйте numeric_cast из Boost.
Синтаксис
numeric_cast очень прост. Это шаблон функции, объявленный следующим образом.
template
inline Target numeric_cast(Source arg)
Если вы уже прочли рецепты 3.1 и 3.3, он аналогичен
lexical_cast. У него имеется два параметра шаблона — Target и Source, — которые представляют типы оригинального и результирующего значений. Так как это шаблон функции, компилятор может догадаться о типе аргумента Source, так что требуется указать только Target, как здесь.
int i = 32767;
short s = numeric_cast(i);
short — это аргумент, передаваемый в шаблон как параметр Target. Компилятор догадывается, что Source имеет тип int потому, что i имеет тип int.
В этом случае я впихиваю
int в short. В моей системе (Windows XP) int имеет длину четыре байта, a short — два. short имеет знак, это означает, что для представления числа в нем используется 15 бит и, следовательно, максимальным допустимым положительным значением для него является 32 767. Приведенный выше фрагмент кода работает молча, но когда я увеличиваю i на единицу, она выходит за диапазон short.
s = numeric_cast(i); // Ох!
Вы уже догадались, что выбрасывается исключение
bad_numeric_cast. Смотри остальную часть примера 3.8: numeric_cast также перехватывает потери знака, возникающие при присвоении отрицательного значения со знаком типу без знака.
Но
numeric_cast не решает всех проблем. Если попытаться поместить значение с плавающей точкой в тип без плавающей точки, то будет потеряно все, что находится справа от десятичной точки, так? numeric_cast в этой ситуации не спасает, так что не думайте, что он сможет уберечь вас от всех рискованных предприятий. Например, рассмотрим такой фрагмент кода из примера 3.8:
double a = 3.14;
int i = numeric_cast(d); // Ох!
Здесь не будет выброшено никаких исключений. Но это произойдет, если попробовать такое:
double d = -3.14;
unsigned int ui = numeric_cast(d);
Потому что, несмотря на то что происходит потеря всего, что находится справа от десятичной точки, происходит потеря знака, а это очень плохо.
Рецепты 3.1 и 3.3.
Требуется узнать наибольшее и наименьшее значения, представляемые на данной платформе числовым типом, таким как
int или double.
Чтобы среди прочего получить максимальное и минимальное допустимые значения числового типа, используйте шаблон класса
numeric_limits из заголовочного файла (см. пример 3.9).
Пример 3.9. Получение числовых ограничений
#include
#include
using namespace std;
template
void showMinMax() {
cout << "min: " << numeric_limits::min() << endl;
cout << "max: " << numeric_limits::max() << endl;
cout << endl;
}
int main() {
cout << "short:" << endl;
showMinMax();
cout << "int:" << endl;
showMinMax();
cout << "long:" << endl;
showMinMax();
cout << "float:" << endl;
showMinMax();
cout << "double:" << endl;
showMinMax();
cout << "long double:" << endl;
showMinMax();
cout << "unsigned short:" << endl;
showMinMax();
cout << "unsigned int:" << endl;
showMinMax();
cout << "unsigned long:" << endl;
showMinMax();
}
Вот что я получил в Windows XP, используя Visual C++ 7.1.
short:
min: -32768
max: 32767
int:
min: -2147483648
max: 2147483647
long:
min -2147483648
max 2147483647
float:
min: 1.17549e-038
max: 3.40282e-038
double:
min: 2.22507e-308
max: 1.79769e+308
long double:
min: 2.22507e-308
max: 1.79769e+308
unsigned short:
min: 0
max: 65535
unsigned int:
min: 0
max: 4294967295
unsigned long:
min: 0
max: 4294967295
Пример 3.9 показывает простой пример получения минимального и максимального значений встроенных числовых типов. Шаблон класса
numeric_limits имеет специализации для всех встроенных типов, включая как числовые, так и нечисловые типы. Стандарт требует, чтобы все типы, которые я использовал в примере 3.9, а также перечисленные далее, имели свою специализацию numeric_limits.
bool
char
signed char
unsigned char
wchar_t
min и max — это функции-члены numeric_limits типа static, которые возвращают наименьшее и наибольшее значения для типа переданного им параметра.