Литмир - Электронная Библиотека
A
A

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Всегда проверяйте, насколько точными являются результаты. При вычислениях вы должны представлять себе, каким должен быть результат, иначе столкнетесь с глупой ошибкой или ошибкой вычислений. Помните об ошибках округления и, если сомневаетесь, обратитесь за советом к эксперту или почитайте учебники по численным методам.

ПОПРОБУЙТЕ

Замените в примере число 333 числом 10 и снова выполните программу. Какой результат следовало ожидать? Какой результат вы получили? А ведь мы предупреждали!

Влияние фиксированного размера целых чисел может проявиться более резко. Дело в том, что числа с плавающей точкой по определению являются приближениями действительных чисел, поэтому они могут терять точность (т.е. терять самые младшие значащие биты). С другой стороны, целые числа часто переполняются (т.е. теряют самые старшие значащие биты). В итоге ошибки, связанные с числами с плавающей точкой, имеют более сложный характер (которые новички часто не замечают), а ошибки, связанные с целыми числами, бросаются в глаза (их трудно не заметить даже новичку). Мы предпочитаем, чтобы ошибки проявлялись как можно раньше, тогда их легче исправить.

Рассмотрим целочисленную задачу.

short int y = 40000;

int i = 1000000;

cout << y << " " << i*i << "\n";

Выполнив эту программу, получим следующий результат:

–25536 –727379968

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

short
не может представить число 40 000, а четырехбайтовое число типа
int
не может представить число 1 000 000 000 000. Точные размеры встроенных типов в языке C++ (см. раздел A.8) зависят от аппаратного обеспечения и компилятора; размер переменной
x
или типа
x
в байтах можно определить с помощью оператора
sizeof(x)
. По определению
sizeof(char)==1
. Это можно проиллюстрировать следующим образом.

Программирование. Принципы и практика использования C++ Исправленное издание - _290.png

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Эти размеры характерны для операционной системы Windows и компилятора компании Microsoft. В языке С++ есть много способов представить целые числа и числа с плавающей точкой, используя разные размеры, но при отсутствии важных причин лучше придерживаться типов
char
,
int
и
double
. В большинстве программ (но, разумеется, не во всех) остальные типы целых чисел и чисел с плавающей точкой вызывают больше проблем, чем хотелось бы.

Целое число можно присвоить переменной, имеющей тип числа с плавающей точкой. Если целое число окажется больше, чем может представить тип числа с плавающей точкой, произойдет потеря точности. Рассмотрим пример.

cout << "размеры: " << sizeof(int) << ' ' << sizeof(float) << '\n';

int x = 2100000009; // большое целое число

float f = x;

cout << x << ' ' << f << endl;

cout << setprecision(15) << x << ' ' << f << '\n';

На нашем компьютере мы получили следующий результат:

Sizes: 4 4

2100000009 2.1e+009

2100000009 2100000000

Типы

float
и
int
занимают одинаковое количество памяти (4 байта). Тип
float
состоит из мантиссы (как правило, числа от нуля до единицы) и показателя степени (т.е. мантисса*10 показатель степени), поэтому он не может точно выразить самое большое число
int
. (Если бы мы попытались сделать это, то не смогли бы выделить достаточно памяти для мантиссы после размещения в памяти показателя степени.) Как и следовало ожидать, переменная
f
представляет число 2100000009 настолько точно, насколько это возможно. Однако последняя цифра
9
вносит слишком большую ошибку, — именно поэтому мы выбрали это число для иллюстрации.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 С другой стороны, когда мы присваиваем число с плавающей точкой перемен- ной целочисленного типа, происходит усечение; иначе говоря, дробная часть — цифры после десятичной точки — просто отбрасываются. Рассмотрим пример.

float f = 2.8;

int x = f;

cout << x << ' ' << f << '\n';

Значение переменной

x
будет равно
2
. Оно не будет равным
3
, как вы могли подумать, если применили “правило округления 4/5”. В языке C++ преобразование типа
float
в тип
int
сопровождается усечением, а не округлением.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 При вычислениях следует опасаться возможного переполнения и усечения.

Язык C++ не решит эту проблему за вас. Рассмотрим пример.

void f(int i, double fpd)

{

 char c = i;      // да: тип char действительно представляет

                  // очень маленькие целые числа

 short s = i;     // опасно: переменная типа int может

                  // не поместиться

                  // в памяти, выделенной для переменной

                  // типа short

 i = i+1;         // что, если число i станет максимальным?

 long lg = i*i;   // опасно: переменная типа long не может

                  // вместить результат

331
{"b":"847443","o":1}