W3docs

Числа в Java

Работа с числовыми типами в Java — int, long, float, double — точность, переполнение и класс Number.

Java различает целые и вещественные числа, и в каждом семействе есть несколько размеров. Выбор правильного типа важен как для корректности (точность, диапазон), так и для производительности. Эта глава — сжатый обзор числовых типов, их литералов, преобразований и подводных камней, которые поджидают начинающих. Предполагается, что вы уже знаете, как объявить переменную, и знакомы с полным набором примитивных типов данных.

Числовые примитивы

ТипБитыДиапазонПример литерала
byte8-128 до 127byte b = 10;
short16-32 768 до 32 767short s = 30_000;
int32-2³¹ до 2³¹-1 (≈ ±2,1 млрд)int i = 1_000_000;
long64-2⁶³ до 2⁶³-1long l = 9_000L;
float32IEEE 754 одинарной точности (~7 цифр)float f = 3.14f;
double64IEEE 754 двойной точности (~16 цифр)double d = 3.14;

В повседневном Java:

  • Используйте int для счётчиков, индексов и большинства целых значений.
  • Используйте long, когда значение может превысить ~2 миллиарда (размеры файлов, миллисекунды с начала эпохи, генераторы идентификаторов).
  • Используйте double для вычислений с плавающей точкой.
  • Используйте float только при нехватке памяти или когда этого требует API.

Целочисленные литералы

Обычные цифры — это int:

int answer = 42;
int million = 1_000_000;     // underscores are ignored

Суффикс L (предпочтительно в верхнем регистре) для long:

long big = 9_000_000_000L;

Префикс 0x для шестнадцатеричного, 0b для двоичного, 0 для восьмеричного:

int hex = 0xFF;        // 255
int bin = 0b1010;      // 10
int oct = 0755;        // 493 — be careful, easy to write by accident

(Восьмеричные литералы — частый источник ошибок: 0123 — это не 123, а 83. Избегайте ведущих нулей, если вы действительно не имеете в виду восьмеричное число.)

Литералы с плавающей точкой

Обычные десятичные числа — это double:

double pi = 3.14159;
double e = 2.71828;

Суффикс f для float, d для явного double (необязательно):

float pif = 3.14f;
double pid = 3.14d;

Научная нотация тоже работает:

double avogadro = 6.022e23;
double tiny = 1.0e-9;

Преобразование числовых типов

Java выполняет расширяющее преобразование автоматически, когда меньший тип переходит в больший (данные не теряются):

int x = 7;
double promoted = x;   // 7.0 — int widens to double automatically

Обратное направление — сужающее: значение может не поместиться, поэтому Java требует явного приведения и молча отбрасывает то, что не умещается. Усечение, а не округление:

double d = 9.99;
int i = (int) d;       // 9 — the fraction is dropped, never rounded

long big = 9_000_000_000L;
int truncated = (int) big;   // 410065408 — high bits lost, value is garbage

Поэтому сужайте тип только тогда, когда уверены, что значение поместится. Чтобы округлить double до ближайшего целого вместо усечения, используйте Math.round (см. Java Math).

Преобразования также происходят внутри выражений. Целочисленное деление отбрасывает остаток, что удивляет начинающих:

System.out.println(7 / 2);     // 3   — both operands are int, so int division
System.out.println(7 / 2.0);   // 3.5 — one double operand promotes the whole expression

Полные правила явного приведения типов см. в Java Type Casting.

Переполнение

Целочисленная арифметика молча оборачивается по границам типа. Исключение не выбрасывается:

int max = Integer.MAX_VALUE;
System.out.println(max + 1);   // -2147483648 (Integer.MIN_VALUE)

Для арифметики с проверкой переполнения используйте семейство Math.exact:

Math.addExact(Integer.MAX_VALUE, 1);   // throws ArithmeticException
Math.multiplyExact(100_000, 100_000);  // throws too

Полезно, когда корректность важнее производительности — финансовые расчёты, размеры выделяемой памяти и т. д.

Точность чисел с плавающей точкой

double и float используют формат IEEE 754 с двоичным представлением — они не могут точно представить большинство десятичных дробей:

System.out.println(0.1 + 0.2);   // 0.30000000000000004

Никогда не сравнивайте числа с плавающей точкой с помощью ==. Сравнивайте с допуском:

double diff = Math.abs(a - b);
if (diff < 1e-9) { /* close enough */ }

Для денежных сумм или в любом месте, где нужна точная десятичная арифметика, используйте BigDecimal:

import java.math.BigDecimal;

BigDecimal sum = new BigDecimal("0.1").add(new BigDecimal("0.2"));
System.out.println(sum);   // 0.3

Всегда передавайте строки в new BigDecimal(...). Конструктор с double заново внесёт двоичную неточность.

Специальные значения с плавающей точкой

У double есть три значения, которые не являются обычными числами:

double posInf = Double.POSITIVE_INFINITY;
double negInf = Double.NEGATIVE_INFINITY;
double nan = Double.NaN;

System.out.println(1.0 / 0);          // Infinity
System.out.println(-1.0 / 0);         // -Infinity
System.out.println(0.0 / 0);          // NaN

System.out.println(nan == nan);       // false — NaN compares unequal to everything
System.out.println(Double.isNaN(nan)); // true

Используйте Double.isNaN(x) для проверки на NaN; == не сработает.

Классы-обёртки

Для каждого примитива есть соответствующая обёртка: Byte, Short, Integer, Long, Float, Double. Обёртки расширяют абстрактный класс Number. Они нужны, когда:

  • Используются коллекции, хранящие объекты: List<Integer>, Map<String, Double>.
  • Выполняется разбор строк: Integer.parseInt("42"), Double.parseDouble("3.14").
  • Выполняется преобразование в другой числовой тип через .intValue(), .doubleValue() и т. д.

Java автоматически выполняет автоупаковку между примитивом и обёрткой:

List<Integer> ids = new ArrayList<>();
ids.add(42);            // autobox: int → Integer
int first = ids.get(0); // unbox: Integer → int

BigInteger и BigDecimal

Когда числа могут быть больше long или необходима десятичная точность:

import java.math.BigInteger;

BigInteger huge = new BigInteger("123456789012345678901234567890");
huge.multiply(huge);

BigDecimal price = new BigDecimal("19.95");
BigDecimal tax = price.multiply(new BigDecimal("0.07"));

Они медленнее примитивов, но в случаях, когда они нужны, это оправданный компромисс.

Демонстрация

java— editable, runs on the server

Что дальше

  • Java MathMath.abs, Math.round, Math.pow и остальные инструменты числовых вычислений.
  • Java Operators — как арифметические, сравнительные и побитовые операторы работают с этими типами.
  • Java Booleans — двузначный тип Java, намного проще в объяснении.

Практика

Практика
Какой тип наиболее подходит для хранения денежной суммы в Java?
Какой тип наиболее подходит для хранения денежной суммы в Java?
Was this page helpful?