Разница между float и double

🔥 🚀 Важно для всех, кто работает с 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 Code

float теряет точность уже после 7 знаков, а double сохраняет значение без искажений.

Диапазон значений

Максимальные (и минимальные) значения:

  • float: от -3,4 × 10^38 до 3,4 × 10^38
  • double: -1.7 × 10^308 до 1.7 × 10^308

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

Краткое сравнение

Параметрfloatdouble
Размер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 Code

float как бы игнорирует такие маленькие прибавления, тогда как 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 так же быстро. Важная разница появляется, когда:

  1. Скорость ограничена памятью (загрузка/хранение большого количества чисел),
  2. Используется специальное оборудование — видеокарты или мобильные чипы,
  3. Применяются 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».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх