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(вещественное_число)
Примеры:
Команда | Результат |
---|---|
|
|
|
|
|
|
Метод Math.ceil()
Метод Math.ceil()
округляет число до целого вверх, примеры:
Команда | Результат |
---|---|
|
|
|
|
|
|
Метод Math.floor()
Метод Math.floor()
округляет число до целого вниз, примеры:
Команда | Результат |
---|---|
|
|
|
|
|
|
Хотя, для округления числа до целого вниз, будет проще использовать просто оператор приведения типа — (int)
:
Команда | Результат |
---|---|
|
|
Если вам сложно запомнить эти команды, вам поможет небольшой урок английского:
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
.
Вычитание чисел слишком разных размерностей | Объяснение |
---|---|
|
Второе число слишком маленькое, и его значащая часть игнорируется (выделено серым). Оранжевым выделены 15 значащих цифр. |
Что тут сказать, программирование — это не математика.
4. Опасность сравнения вещественных чисел
Еще одна опасность подстерегает программистов при сравнении вещественных чисел. Т.к. при работе с этими числами могут накапливаться ошибки округления, то возможны ситуации, когда вещественные числа должны быть равны, но они не равны. И наоборот: числа должны быть не равны, но они равны.
Пример:
Команда | Пояснение |
---|---|
|
В переменной a будет значение 1000000000.0 В переменной c будет значение 1000000000.0 (число в переменной b слишком маленькое)
|
В приведенном выше примере a
и c
не должны быть равны, но они равны.
Или возьмем другой пример:
Команда | Пояснение |
---|---|
|
В переменной a будет значение 1.0 В переменной b будет значение 1.0
|
5. Интересный факт о strictfp
В Java есть специальное ключевое слово strictfp
(strict floating point), которого нет в других языках программирования. И знаете, зачем оно нужно? Оно ухудшает точность работы с вещественными числами. История его появления примерно такова:
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ