JavaRush /Курсы /Java Syntax Pro /Нюансы работы с вещественными числами

Нюансы работы с вещественными числами

Java Syntax Pro
4 уровень , 7 лекция
Открыта

1. Округление вещественных чисел

Как мы уже разбирали, при присваивании переменной типа int вещественного числа оно всегда округляется вниз до целого — его дробная часть просто отбрасывается.

А ведь легко можно представить ситуацию, когда дробное число нужно округлить просто до ближайшего целого или вообще вверх. Что делать в этой ситуации?

Для этого и для многих похожих случаев в Java есть класс Math, у которого есть методы round(), ceil(), floor().


Метод Math.round()

Метод Math.round() округляет число до ближайшего целого:

long x = Math.round(вещественное_число)

Но, как говорится, есть нюанс: результат работы этого метода — целочисленный тип long (не int). Вещественные числа ведь могут быть очень большими, поэтому разработчики Java решили использовать самый большой целочисленный тип, который есть в Java — long.

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

int x = (int) Math.round(вещественное_число)

Примеры:

Команда Результат
int x = (int) Math.round(4.1);
4
int x = (int) Math.round(4.5);
5
int x = (int) Math.round(4.9);
5

Метод Math.ceil()

Метод Math.ceil() округляет число до целого вверх, примеры:

Команда Результат
int x = (int) Math.ceil(4.1);
5
int x = (int) Math.ceil(4.5);
5
int x = (int) Math.ceil(4.9);
5

Метод Math.floor()

Метод Math.floor() округляет число до целого вниз, примеры:

Команда Результат
int x = (int) Math.floor(4.1);
4
int x = (int) Math.floor(4.5);
4
int x = (int) Math.floor(4.9);
4

Хотя, для округления числа до целого вниз, будет проще использовать просто оператор приведения типа — (int):

Команда Результат
int x = (int) 4.9
4

Если вам сложно запомнить эти команды, вам поможет небольшой урок английского:

  • Math — математика
  • Round — круг/округлять
  • Ceiling — потолок
  • Floor — пол


2. Устройство чисел с плавающей точкой

Тип double может хранить значения в диапазоне -1.7*10308 до +1.7*10308. Такой гигантский диапазон значений (по сравнению с типом int) объясняется тем, что тип double (как и float) устроен совсем иначе по сравнению с целыми типами. Каждая переменная типа double содержит два числа: первое называется мантисса, а второе — степень.

Допустим, у нас есть число 123456789, и мы сохранили его в переменную типа double. Тогда число будет преобразовано к виду 1.23456789*108, и внутри типа double будут храниться два числа — 23456789 и 8. Красным выделена «значащая часть числа» (манти́сса), синим — степень.

Такой подход позволяет хранить как очень большие числа, так и очень маленькие. Но т.к. размер числа ограничен 8 байтами (64 бита) и часть бит используется под хранение степени (а также знака числа и знака степени), максимальная длина мантиссы ограничена 15 цифрами.

Это очень упрощенное описание устройства вещественных чисел, более полное можно найти по ссылке.


3. Потеря точности при работе с вещественными числами

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

Последнее — самая неожиданная ситуация для новичков в программировании.

Если из числа 109 вычесть 1/109, мы получим опять 109.

Вычитание чисел слишком разных размерностей Объяснение
 1000000000.000000000;
-         0.000000001;
 1000000000.000000000;
Второе число слишком маленькое, и его значащая часть игнорируется (выделено серым). Оранжевым выделены 15 значащих цифр.

Что тут сказать, программирование — это не математика.


4. Опасность сравнения вещественных чисел

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

Пример:

Команда Пояснение
double a = 1000000000.0;
double b = 0.000000001;
double c = a - b;
В переменной a будет значение 1000000000.0
В переменной c будет значение 1000000000.0
(число в переменной b слишком маленькое)

В приведенном выше примере a и c не должны быть равны, но они равны.

Или возьмем другой пример:

Команда Пояснение
double a = 1.00000000000000001;
double b = 1.00000000000000002;
В переменной a будет значение 1.0
В переменной b будет значение 1.0

5. Интересный факт о strictfp

В Java есть специальное ключевое слово strictfp (strict floating point), которого нет в других языках программирования. И знаете, зачем оно нужно? Оно ухудшает точность работы с вещественными числами. История его появления примерно такова:

Создатели Java:
Мы очень хотим, чтобы Java была суперпопулярна, и программы на Java выполнялись на как можно большем количестве устройств. Поэтому мы прописали в спецификацию Java-машины, что на всех типах устройств все программы должны выполняться одинаково!
Создатели процессора Intel:
Ребята, мы улучшили наши процессоры, и теперь все вещественные числа внутри процессора будут представлены не 8-ю, а 10-ю байтами. Больше байт — больше знаковых цифр. А это значит что? Правильно: теперь ваши научные вычисления будут еще более точными!
Ученые и все, кто занимается сверхточными расчетами:
Круто! Молодцы. Отличная новость.
Создатели Java:
Не-не-не, ребята. Мы же сказали: все Java-программы должны выполняться одинаково на всех устройствах. Принудительно выключаем возможность использования 10 байтовых вещественных чисел внутри процессоров Intel.
Вот теперь все опять отлично! Не благодарите.
Ученые и все, кто занимается сверхточными расчетами:
Да вы там совсем охренели? Ану быстро вернули все как было!
Создатели Java:
Ребята, это для вашей же пользы! Только представьте: все Java-программы выполняются одинаково на всех устройствах. Ну круто же!
Ученые и все, кто занимается сверхточными расчетами:
Нет. Совсем не круто. Быстро вернули все обратно! Или мы вашу Java вам знаете куда засунем?
Создатели Java:
Гм. Что же вы сразу не сказали. Конечно, вернем.
Вернули возможность пользоваться всеми фичами крутых процессоров.
Кстати. Мы так же специально добавили в язык слово strictfp: если его написать перед именем функции, вся работа с вещественными числами внутри этой функции будет одинаково плохой на всех устройствах!
Комментарии (362)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Анна Клушкина Уровень 11
1 января 2026
Lena Уровень 10
21 декабря 2025
public class Solution { public static void main(String[] args) { double glass = 0.5; //напишите тут ваш код Scanner scanner = new Scanner (System.in); boolean answer = scanner.nextBoolean(); if(answer){ System.out.println((int) Math.ceil(glass)); } else System.out.println((int) Math.floor(glass)); } }
Ramzan Уровень 6
31 октября 2025
Мне одному показалась смешной ссора между разработчиками Java и учеными?
Artur Уровень 14
17 октября 2025
почему 15 не целое число разве ?? Я что то не понимаю
Horror Уровень 6
13 августа 2025
Вот моя задача: Программа должна считать вещественное число real и два целых числа a и b. Программа должна определить к какому целому числу (a или b) вещественное real ближе. Программа должна округлить вещественное real в ту сторону к которому будет ближе. Если a и b равны, то округлять нужно в ближайшую сторону по правилам математики. Программа должна вывести на экран округлённое значение real. Пример: __________________________ Вещественное real = 4,4; Число a = 1; Число b = 10; Вывод на экран: 4 __________________________ Пример с отрицательным вводом: real = - 1,5; a = 2; b = 3; Вывод: 2.
DM Уровень 10
3 августа 2025
Scanner sc = new Scanner(System.in); System.out.println(sc.nextBoolean() ? (int) Math.ceil(glass) : (int) Math.floor(glass));
3 июня 2025
Решение задачи про стакан:
 
                Scanner sc = new Scanner (System.in);
        boolean isTrueOrFalse = sc.nextBoolean();             
        glass = isTrueOrFalse==true?Math.ceil(glass):Math.floor(glass);                                                                                                        
        System.out.println ((int)glass);
 
Решение задачи скорости ветра:
 
        Scanner sc = new Scanner (System.in);
        int speed = (int)Math.round(sc.nextInt ()*3.6); 
        System.out.println (speed);
 
Soda Уровень 7
9 декабря 2025
умно, я забыл про существования тернарного оператора
3 июня 2025
"Для пессимиста он наполовину пуст, а для оптимиста - наполовину полон." - для логика и реалиста - одновремнно на половину пуст и на половину полон.
Виктор Уровень 9
3 июля 2025
для жаждущего выпит для спящего не тронут для умного налит для глупого разбит
3 июня 2025
"Если из числа 10^9 вычесть 1/10^9, мы получим опять 10^9." - и что я должен из этого понять? Почему? Каким образом "слишком маленькое" объясняет суть? Что такое мантисса я знаю, однако тут сказали А, но забыли сказать Б. Зачем начинать, что докончить не можете? Мне сейчас что делать? Где разбор? Если минимальный диапазон от -1.7*10^308 и выше, а 0.хххх точно выше, и при этом степень за рамки 308 не выходит, почему оно не может поместиться? Если дело в бесконечной дроби, то опять вопрос, с чего это вдруг там будут одни нули, если она бесконечная, ведь при урезании, все равно что то останется, следовательно оно и вычтется, но почему то результат как будто там одни нули. Реально сами задради тему и теперь убегают от объсянения.
Horror Уровень 6
12 августа 2025
авторы предположили, что никто это читать не будет. я тоже удивился от поданной информации и пошёл терроризировать нейросети для разъяснения этой темы.
Dmitry Terekhov Уровень 23
17 августа 2025
как по мне досточто подробно сказали, что имеют значение только первые 15 символов
Anonymous #3585174 Уровень 33
31 мая 2025
good