§4 Действительный тип. Библиотека cmath. Преобразование типов

Действительные типы

Действительных или вещественных типов всего три: float, double и long double:

Types       Size MIN                           MAX                           
float       4    1.1754944e-38                 3.4028235e+38                 
double      8    2.225073858507201e-308        1.797693134862316e+308        
long double 12   3.3621031431120935063e-4932   1.189731495357231765e+4932

Вещественные типы могут быть представлены следующим способом:

double mu = 1.91315;
double me = .5110034;
double d = 2.;
double c = 3E8;
double G = 6.6720e-11;

(Для этого послужили значения некоторых известных физических констант).
Как видно из примера поддерживаются две формы записи: с фиксированной точкой (1, 2 и третья строка) и экспоненциальная (иначе научный формат или число с плавающей точкой). Обратите внимание на то, что для обеих форм нулевую дробную или целую часть числа (для экспоненциальной формы – в мантиссе) можно опустить (но не обе одновременно!). Также можно опустить и саму точку, оставив лишь целую часть числа. Но для улучшения представления кода точку всё же опускать не следует. Для представления отрицательной вещественной константы используется оператор “унарный минус”.

Библиотека cmath

Большой набор математических функций становится доступным, если подключить библиотеку cmath. Некоторые часто используемые функции перечислены в методичке. Все эти функции возвращают тип соответствующий типу аргументов. Функции, принимающие или возвращающие угол, работают с радианами (напоминаю, что  \alpha[rad] = \alpha[\textdegree]\times(\pi/180) ).
Решим следующую задачу: По двум катетам прямоугольного треугольника произвести полный расчет параметров треугольника. Найти: гипотенузу, радиусы вписанной и описанной окружностей, площадь и периметр.
Программа 4.1

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

int main() {
	double a, b;
	cout << "a = "; cin >> a;
	cout << "b = "; cin >> b;
	double c = sqrt(a * a + b * b); // или hypot(a, b);
	double S = 0.5 * a * b;
	double P = a + b + c;
	double R = c / 2;
	double r = (a + b - c) / 2;
	cout << setprecision(3)
		 << fixed
		 << "c = " << setw(7) << c << "\n"
		 << "S = " << setw(7) << S << "\n"
		 << "P = " << setw(7) << P << "\n"
		 << "R = " << setw(7) << R << "\n"
		 << "r = " << setw(7) << r << endl;
	return 0;
}

Вывод программы

a = 17
b = 19
c =  25.495
S = 161.500
P =  61.495
R =  12.748
r =   5.252

Точность вычислений

Размеры мантиссы (дробной части) чисел действительных типов float, double и long double, в количестве десятичных разрядов, соответственно равны: 7, 15 и 19. Это предел точности этих типов, сверх которой, будет накапливаться ошибка. Отсюда следует, что наибольшей точностью вычислений обладает тип long double.
Науке известны как слишком огромные величины, так и слишком маленькие: Объем Метагалактики  3,5\cdot10^{30} m^3 , радиус протона  0,8768\cdot10^{-15} m , масса электрона  9.10938291\cdot10^{-31} kg , масса Солнца 1.98892\cdot10^{30} kg , а Галактики, и того больше – 6\cdot10^{42} kg!! Очевидно, что производя вычисления с подобными числами мы будем терять точность вычислений, так как в мантиссе представлено лишь небольшое число десятичных разрядов по сравнению с порядком. Представьте, что нужно рассчитать точную траекторию полета межпланетной космической станции с посадкой, скажем, на ядро кометы (которое, собственно, не очень велико; в любом случае – не сопоставимые как с расстоянием до Земли, так и с размерами самой Земли). Вычисления должны производиться максимально точно, иначе наш проект потерпит фиаско! (Современная космонавтика уже способна производить “ювелирные” операции в просторах межпланетного пространства. Примером может служить посадка на ядро кометы Черюмова-Герасименко модуля «Филы» космического аппарата “Розетта” произошедшая 12.11.2014 г.)
Если необходимо соблюсти точность вычислений при работе с целыми числами, то не нужно использовать для этих целей любой из вещественных типов! Необходимо выбирать подходящий по размеру целый тип. Если же размер числа больше любого целого типа, то прибегать к программированию больших чисел с помощью алгоритмов длинной арифметики с массивами.
Заметим, что для точных вычислений с большими числами (как с целыми, так и с действительными) для языка С++ существуют несколько свободных библиотек, например GMP и MPIR.

Преобразования типа в арифметических выражениях

В арифметических операциях с целыми числами происходит неявное преобразование типа. Ввиду наличия знаковых и беззнаковых разновидностей целых типов преобразование будет происходить по следующим правилам:

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

Это, например, можно изобразить следующим образом:
int + unsigned long => long long
Проиллюстрируем это на примере:
Программа 4.2

#include <iostream>
using namespace std;

int main() {

	long			d = 2147483647;
	unsigned long	e = 4294967295;

	cout << d + e << endl;
	cout << (long long)d + e << endl;

	return 0;
}

Результат:

2147483646
6442450942

Программа должна сложить два числа, однако значения чисел являются граничными для соответствующих типов (long и unsigned long). В результате в первой строке ответ неверный! Во второй строке предусмотрен такой исход. Создаётся копия переменной, которая приводится к типу, который имеет больший диапазон, то есть long long. Такое преобразование типов называется явным. Результат – правильный, но обратите внимание, что программа продолжила работу как ни в чем не бывало! Причина кроется в представлении целых чисел в памяти компьютера.
Формат преобразования типа в программе 4.2 является устаревшей формой явного преобразования типа в стиле C, который существует до сих пор по причине совместимости.
Еще один способ преобразования – в виде вызова функции:

cout << 15 / 2 << endl; // результат 7
cout << float(15) / 2 << endl; // результат 7.5

В С++ рекомендуется использовать новый формат преобразования типов. Он выглядит следующим образом:

static_cast<type>(expression)

Разумеется, в выражениях могут присутствовать как целые, так и вещественные числа. Однако, нужно помнить, что результат вычислений будет всегда вещественный! Следовательно, необходимо использовать любой вещественный тип переменной для сохранения результата. Если для сохранения используется вещественный тип с меньшим размером, то произойдет преобразование типа (с возможной потерей точности). Рассмотрим подробнее на примере.
Программа 4.3

#include <iostream>
using namespace std;

int main()
{
    int a = 2000;
	long long b = 100;
	float c = 7.0;
	double d = 4.5;
	cout << sizeof(b + a) << " -> " << b + a << endl;
	cout << sizeof(c * d) << " -> " << c * d << endl;
	cout << sizeof(a / int(d)) << " -> " << a / int(d) << endl;
	cout << sizeof(b / c) << " -> " << b / c << endl;

    return 0;
}

Результат работы программы:

8 -> 2100
8 -> 31.5
4 -> 500
4 -> 14.2857

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

Вопросы

  1. Перечислите формы записи вещественных чисел.
  2. Почему в процессе вычислений с вещественными числами падает точность вычислений?
  3. Какие действительные типы вам известны? Какой тип наиболее точный при вычислениях?
  4. Какой тип данных мы получим если целое число разделим на вещественное? Вещественное на целое?
  5. Когда нужно выбирать для вычислений целый, а когда действительный тип?

Практические задания

1. Составить программу. Даны длины сторон треугольника a, b, c. Найти длины высот. Длина высоты вычисляется по формуле:
 h_a = \frac{2S}{a} , где S - площадь треугольника (вычисляется по формуле Герона).
2. Составить программу. По заданным величинам радиусов оснований r и R и высоты h найти объем и площадь поверхности усеченного конуса.

Ссылки по теме

» Функции. Заголовочный файл
» Что нужно знать про арифметику с плавающей запятой
» Алгоритмы на С++ (олимпиадный подход)


Print Friendly, PDF & Email

Comments are closed.