🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
В этой статье рассмотрим, в чем разница между float и double в языке Java — что выбрать, когда и почему. Вы можете выбрать удобный для вас формат — посмотреть видеоурок на английском языке, либо же прочитать текстовую версию ниже.
Даже у людей, которые уже какое-то время пишут код на Java, не всегда есть понимание, чем отличаются float
и double
.
И дело вот в чем: и float
, и double
работают с десятичными числами в Java, но тем не менее между ними есть разница. Выбор неправильного варианта может привести к потере точности или к неоправданному расходу памяти. Float
использует 32 бита, double
— 64. Звучит просто, но на практике эта разница может оказаться критичной. Прочитайте эту статью до конца, и вы точно будете знать, когда стоит использовать каждый из них — а когда лучше вовсе отказаться.
Что такое float и double в Java?
Давайте начнем с основ — что это вообще такое?
Float в Java
Итак, float
— это 32-битное (4 байта) число с плавающей точкой, реализованное по стандарту IEEE 754 (если по-простому — это способ, которым компы хранят дробные числа). Он занимает меньше памяти, но и точность у него ограниченная. Уместно сравнить его с компактной машиной — быстрый и маленький.
Вот как его можно объявить:
float myFloat = 3.14f; // That 'f' is your VIP pass—don't skip it!
Или альтернативный вариант:
float anotherFloat = (float) 3.14; // Casting works too
Почему стоит обратить внимание на суффикс “f”? Потому что по умолчанию Java считает все числа с десятичной точкой типом double
. И если попытаться присвоить такое значение переменной float
без приведения или f
, компилятор выдаст ошибку:
float oops = 3.14; // Nope—compiler's like, "What even is this?"
Double в Java
Теперь перейдем к double
— это уже “взрослый” тип с плавающей точкой. Он занимает 64 бита (8 байт) и обеспечивает двойную точность (отсюда и название), также как и float
реализован по стандарту IEEE 754. Именно double
используется по умолчанию для всех десятичных чисел в Java:
double myDouble = 3.14; // No fuss, no suffix
Можно также добавить суффикс “d”, но это необязательно:
double anotherDouble = 3.14d; // 'd' is optional but cool
Как это работает
Оба типа хранят числа в бинарном виде: 1 бит для знака (положительное/отрицательное число), несколько бит для экспоненты (определяет насколько большое или маленькое число), а остальное — на мантиссу (значащие цифры). Флоат получает 23 бита для этой последней части, дабл – 52. Больше битов – больше деталей. Однажды я отлаживал 3D-игру приятеля, в которой при плавании края вихлялись как желе – переключился на двойной режим, и бац – гладко как по маслу.
Читайте также: 11 полезных фишек для Java
Основные различия между float и double
Давайте сравним float
и double
.
Объем занимаемой памяти
Первое отличие — объем:
float
: 4 байта (32 бита)double
: 8 байт (64 бита)
Если вы работаете с большим количеством чисел (например, при обработке изображений), float
может существенно сэкономить память.
Точность
И вот в чем кроется ключевое отличие — точность:
float
: примерно 7 знаков после запятойdouble
: 15–16 знаков
Посмотрите сами:
float myFloat = 1.123456789f;
double myDouble = 1.123456789;
System.out.println("Float: " + myFloat); // 1.1234568
System.out.println("Double: " + myDouble); // 1.123456789
Run Codefloat
теряет точность уже после 7 знаков, а double
сохраняет значение без искажений.
Диапазон значений
Максимальные (и минимальные) значения:
float
: от -3,4 × 10^38 до 3,4 × 10^38double
: -1.7 × 10^308 до 1.7 × 10^308
double
позволяет оперировать числами гигантского масштаба — он подходит для научных вычислений, моделирования, финансов и других задач, где важен большой диапазон.
Краткое сравнение
Параметр | float | double |
Размер | 4 байта (32 бита) | 8 байт (64 бита) |
Точность | ~7 знаков | ~15-16 знаков |
Диапазон | ±3.4 × 10^38 | ±1.7 × 10^308 |
По умолчанию? | Нет — нужен суффикс f | Да — без суффикса |
Использование памяти | Компактный | Более просторный |
Уровень точности | Приемлемый | Отличный |
Пояснение про точность и диапазон
Давайте разбираться, почему точность и диапазон действительно важны.
Проблемы с точностью
Компьютеры не дружат с десятичными дробями — они хранят их в бинарном виде, и это вызывает неожиданные эффекты с числами вроде 0.1. Смотрите сами:
float f = 0.1f + 0.1f + 0.1f;
System.out.println(f); // 0.3 (looks okay)
System.out.println(f == 0.3f); // False—what?!
double d = 0.1 + 0.1 + 0.1;
System.out.println(d); // 0.30000000000000004
Run CodeЭто не совсем 0.3 — все-таки ближе к 0.300000001. Поэтому не сравнивайте дробные числа через ==
. Лучше делайте так:
boolean closeEnough = Math.abs(f - 0.3f) < 0.0001f; // True
Точность в действии
Вот интересный пример:
float floaty = 0.1234567f;
double doubly = 0.1234567;
System.out.println("Float starts: " + floaty); // 0.1234567
System.out.println("Double starts: " + doubly); // 0.1234567
for (int i = 0; i < 10; i++) {
floaty += 0.0000001f;
doubly += 0.0000001;
}
System.out.println("Float ends: " + floaty); // 0.12345671
System.out.println("Double ends: " + doubly); // 0.1234568
Run Codefloat
как бы игнорирует такие маленькие прибавления, тогда как double
улавливает все изменения и учитывает их.
Переполнение и “утечка” значений
Слишком большое число?
float bigFloat = 3.4e38f;
double bigDouble = 1.7e308;
System.out.println(bigFloat * 10f); // Infinity
System.out.println(bigDouble * 10); // Infinity
Run CodeА если слишком маленькое?
float tinyFloat = 1.4e-45f;
double tinyDouble = 4.9e-324;
System.out.println(tinyFloat / 10f); // 0.0
System.out.println(tinyDouble / 10); // 0.0
Run CodeУ них есть лимиты — переступил через них, и вычисления обнуляются.
Когда использовать float, а когда double
Итак, когда же стоит выбирать каждый из них?
Используйте float, если:
- Мало памяти: большие массивы?
float
вдвое снижает нагрузку. - “Капризное” оборудование: мобильные устройства и встраиваемые системы часто предпочитают 32-битные
float
. - Работа с графикой: 3D-движки любят
float
— быстро и достаточно точно. - 7 знаков после запятой хватает: зачем усложнять, если не нужна сверхточность?
Выбирайте double, если:
- Точность важнее всего: применяйте в науке, финансах — везде, где маленькие ошибки перерастают в большие потери.
- Нужно работать с очень большими или очень маленькими числами:
double
справляется с крайними значениями. - Не хотите заморачиваться: Java по умолчанию предпочитает double — довольно удобно.
- Не уверены: если памяти достаточно, double надежнее.
- Про деньги: На самом деле, ни то, ни другое не подходит для денежных расчетов (но об этом позже).
Пример из игровой физики
Для игр лучше использовать float
:
public class GameMove {
public static float[] moveIt(float x, float y, float vx, float vy, float time) {
x += vx * time;
y += vy * time;
return new float[] {x, y};
}
}
Run CodeПамять не перегружается, даже когда на экране сотни объектов.
Пример из науки
Для задач вроде расчета орбит вполне подойдет double
:
public class SpaceMath {
private static final double G = 6.67430e-11;
public static double orbitSpeed(double mass, double radius) {
return Math.sqrt((G * mass) / radius);
}
}
Run CodeВ этом деле важна точность!
Проверка производительности
Есть мнение, что float
всегда быстрее double
. На современных 64-битных процессорах это не так однозначно — они часто обрабатывают double
так же быстро. Важная разница появляется, когда:
- Скорость ограничена памятью (загрузка/хранение большого количества чисел),
- Используется специальное оборудование — видеокарты или мобильные чипы,
- Применяются SIMD-инструкции, где float помещается в регистр в большем количестве.
Типичные ошибки и варианты решения
Даже профессионалы иногда на этом спотыкаются — будьте внимательны!
Проблемы с деньгами
Использовать float
или double
для денежных расчетов — плохая идея:
double cash = 0.0;
for (int i = 0; i < 10; i++) {
cash += 0.10;
}
System.out.println(cash); // 0.9999999999999999, not 1.0!
Run CodeКлиентам вряд ли понравится “почти один доллар”.
Исправление с помощью BigDecimal
Вот как правильно сделать:
import java.math.BigDecimal;
BigDecimal cash = BigDecimal.ZERO;
BigDecimal dime = new BigDecimal("0.10");
for (int i = 0; i < 10; i++) {
cash = cash.add(dime);
}
System.out.println(cash); // 1.00—perfect
Run CodeМедленнее и требует больше кода, зато без ошибок.
Никакого ==
При сравнении чисел с плавающей запятой лучше использовать допустимую погрешность:
double a = 0.1 + 0.1 + 0.1;
double b = 0.3;
System.out.println(a == b); // False
System.out.println(Math.abs(a - b) < 0.0000001); // True
Run CodeСтранные пограничные случаи
Будьте внимательны:
NaN
: результат0.0 / 0.0
— проверяйте черезDouble.isNaN()
Infinity
: результат1.0 / 0.0
-0.0
: хитрый случай — формально это не то же самое, что0.0
double boom = 1.0 / -0.0;
System.out.println(boom); // -Infinity
Run CodeЗадание для читателей
Теперь попробуйте сами!
Задача на точность
Напишите программу, которая начинает с 1.0
и вычитает 0.1
, пока не достигнет 0
. Подсчитайте, сколько шагов потребуется для float
и для double
. Вот заготовка:
public class DriftTest {
public static void main(String[] args) {
// Go wild—track those counts!
}
}
Run CodeВозможное решение
Как вариант, можно поступить так:
public class DriftTestSolution {
public static void main(String[] args) {
float floatNum = 1.0f;
double doubleNum = 1.0;
int floatSteps = 0;
int doubleSteps = 0;
while (floatNum > 0.0f && floatSteps < 100) {
floatNum -= 0.1f;
floatSteps++;
if (floatNum == 0.0f) break;
}
while (doubleNum > 0.0 && doubleSteps < 100) {
doubleNum -= 0.1;
doubleSteps++;
if (doubleNum == 0.0) break;
}
System.out.println("Float hit 0 at " + floatSteps + ": " + floatNum);
System.out.println("Double after " + doubleSteps + ": " + doubleNum);
}
}
Run CodeИ вот что получается:
Float hit 0 at 10: 0.0
Double after 10: -3.0999999999999996E-16
float
точно попал в ноль, а double
немного промахнулся. Вот и вся точность!
Заключение
А теперь вкратце об отличиях float
и double
в Java:
float
— это 32-битный тип с точностью примерно до 7 десятичных цифр.double
— 64-битный тип с точностью около 15–16 десятичных цифр.double
— в Java это тип по умолчанию для десятичных чисел.- Ни один из типов не может точно представить все десятичные дроби.
- Используйте
float
, когда важен объем использованной памяти. - Используйте
double
в большинстве случаев, особенно когда важна точность. - Используйте
BigDecimal
для финансовых расчетов. - Никогда не используйте
==
для сравнения чисел с плавающей точкой. - На современном оборудовании
double
работает почти так же быстро, какfloat
.
Выбор между этими типами зависит от требований к точности, диапазону, производительности и памяти. Если есть сомнения, стоит протестировать оба варианта, чтобы понять, какой из них работает лучше.
Перевод статьи «Java float vs double».