Бесплатный курс по C++. Зарегистрируйтесь для отслеживания прогресса →

C++: Типы данных с плавающей точкой

Целочисленные типы отлично подходят для подсчета целых чисел, но иногда нам нужно хранить очень большие числа или числа с дробной частью. Переменная типа с плавающей точкой – это переменная, которая может содержать действительное число, например 4320,0, -3,33 или 0,01226. Название плавающая точка указывает на то, что десятичная точка может плавать, то есть она может поддерживать переменное количество цифр до и после себя.

Существует три разных типа данных с плавающей точкой: float, double и long double. Как и в случае с целыми числами, C++ не определяет фактические размеры этих типов, но гарантирует минимальные размеры:

  • float – должен иметь как минимум 32-бита и гарантировать минимум 6 значащих цифр
  • double – 48-бит и быть не меньше float. Гарантирует 15 значащих цифр
  • long double – как минимум 80 битов и 18-значащих цифр

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

Ниже представлены примеры определения переменных с плавающей точкой:

float f_num;
double d_num;
long double ld_num;

Вы могли заметить, что в имени переменной используется префикс - это делать не обязательно, но это помогает понять переменная кого типа сейчас используется.

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

double d_num { 5.0 };
float f_num { 5.0f };

По умолчанию литералы с плавающей точкой имеют тип double. Суффикс f используется для обозначения литерала типа float.

Вы могли заметить, что в коде выше инициализация производится не через оператор присваивания =, а с помощью нотации { 5.0 }. Это списковая инициализация, она пришла в язык с 11 стандарта и она имеет одно преимущество:

int num;
num = 3.14;
std::cout << num; // => 3

В коде выше мы пытаемся сохранить в целочисленную переменную вещественное число. Такая программа скомпилируется и будет работать, но у числа мы потеряем всю вещественную часть.

Если применить списковую инициализацию, получим ошибку компиляции:

int num { 3.14 }; // ошибка
int count;
count = { 3.14 }; // ошибка

Рассмотрим такую программу:

int main() {
  float f_num = { 10.0 / 3.0 };
  double d_num = { 10.0 / 3.0 };
  std::cout << f_num << std::endl;
  std::cout << d_num << std::endl;
}
  3.33333
  3.33333

Обратите внимание, что каждое из напечатанных значений имеет только 6 значащих цифр. Это стандартное поведение объекта cout, но мы можем переопределить это поведение и повысить точность:

#include <iostream>
#include <iomanip> // для увеличения точности

int main() {
  std::cout << std::setprecision(16); // вывод с точностью до 16 цифр

  float f_num = { 10.0 / 3.0 };
  double d_num = { 10.0 / 3.0 };
  std::cout << f_num << std::endl;
  std::cout << d_num << std::endl;
}
  3.333333253860474
  3.333333333333333

Видно, что число float определено не точно и содержит ошибки, поскольку тип float гарантирует нам только шесть значащих цифр.

Существует две особые категории чисел с плавающей точкой. Первая – Inf, которая представляет бесконечность. Inf может быть положительной или отрицательной. Вторая – NaN, что означает Not a Number - не число. Существует несколько различных типов NaN. NaN и Inf доступны только в том случае, если компилятор для чисел с плавающей точкой использует определенный формат. Если используется другой формат, следующий код приводит к неопределенному поведению.

#include <iostream>

int main() {
  double zero { 0.0 };
  double pos_inf { 5.0 / zero };  // положительная бесконечность
  std::cout << pos_inf << std::endl;

  double neg_inf { -5.0 / zero }; // отрицательная бесконечность
  std::cout << neg_inf << std::endl;

  double nan { zero / zero };   // не число (математически неверно)
  std::cout << nan << std::endl;

  return 0;
}
  inf
  -inf
  nan

Задание

Напишите программу, которая рассчитывает сколько километров проедет автомобиль на полном баке. Пусть объем бака будет 43 литра, а расход 8.8 л на 10 км. Результат выведите на экран.

Упражнение не проходит проверку — что делать? 😶

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

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

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя 🤔

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно 🙄

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

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

Полезное


Нашли ошибку? Есть что добавить? Пулреквесты приветствуются https://github.com/hexlet-basics
Если вы столкнулись с трудностями и не знаете, что делать, задайте вопрос в нашем большом и дружном сообществе