Возведение в степень — это умножение числа (основания) на само себя заданное количество раз (показатель степени). В Java нет отдельного оператора для этого (в отличие, например, от Python с **), поэтому используют один из нескольких инструментов: метод Math.pow, обычный цикл, класс BigInteger или побитовый сдвиг. Разберем каждый способ, их границы применимости и типичную ошибку с приведением типов.
Содержание
- Math.pow: базовый способ
- Ловушка 1: переполнение int при приведении из double
- Ловушка 2: потеря точности double на больших целых числах
- Ручной цикл: точный результат для целых чисел
- BigInteger.pow: когда результат не помещается в long
- Побитовый сдвиг: самый быстрый способ для степеней двойки
- Какой способ выбрать
- Выводы
- Где применяется / связь с практикой
- FAQ
Math.pow: базовый способ
Math.pow(double base, double exponent) — статический метод класса Math, который умеет возводить в любую вещественную степень, включая дробную и отрицательную. Вызывать его можно сразу через имя класса, без создания объекта.
public class PowExample {
public static void main(String[] args) {
System.out.println(Math.pow(2, 10));
System.out.println(Math.pow(2, -1));
}
}
Вывод:
1024.0
0.5
Метод всегда возвращает double, даже если на вход пришли целые числа. Если нужен именно целый результат, приходится приводить тип явно:
public static int powInt(int base, int exp) {
return (int) Math.pow(base, exp);
}
Для небольших значений (вроде powInt(2, 10)) это работает корректно и возвращает 1024. Но у такого приведения есть две отдельные ловушки: переполнение диапазона int и потеря точности самого double на больших числах. Разберем обе на реальных числах.
Ловушка 1: переполнение int при приведении из double
Возьмем степень покрупнее — 3 в 30-й степени. Точное значение (можно проверить в столбик или через BigInteger) — 205891132094649. Это уже больше 9 миллиардов, то есть заведомо не влезает в int (диапазон int — от -2147483648 до 2147483647).
Неверный код:
public static int powInt(int base, int exp) {
return (int) Math.pow(base, exp);
}
public static void main(String[] args) {
System.out.println(powInt(3, 30));
}
Фактический результат:
2147483647
Это не переполнение в привычном смысле (когда число «перескакивает» через границу диапазона и продолжает расти с другого края, как бывает при арифметике int + int). Здесь другое правило: при сужающем преобразовании double -> int по спецификации Java (JLS, п. 5.1.3), если вещественное значение выходит за границы int, результатом становится Integer.MAX_VALUE для слишком больших положительных чисел или Integer.MIN_VALUE для слишком больших по модулю отрицательных — без исключения и без предупреждения.
Исправление — использовать более широкий тип и не полагаться на int, если показатель степени заранее не ограничен:
public static long powLong(int base, int exp) {
return (long) Math.pow(base, exp);
}
Для 3 в 30-й степени powLong(3, 30) уже вернет верные 205891132094649 — значение помещается в long (диапазон которого — до 9223372036854775807) и, как показано ниже, здесь double еще способен хранить его точно.
Ловушка 2: потеря точности double на больших целых числах
Тип double хранит 52 бита мантиссы, поэтому точно (без пропусков) представляет подряд идущие целые числа только до 2^53, то есть до 9007199254740992. Число 205891132094649 из примера выше меньше этой границы, поэтому проблем не было. Но если взять 7 в 20-й степени, ситуация меняется.
Точное значение 7^20 (по определению степени, вычисляется без floating-point) — 79792266297612001. Оно все еще помещается в long, но уже больше границы 2^53, поэтому Math.pow не гарантирует точный результат:
public static void main(String[] args) {
long result = (long) Math.pow(7, 20);
System.out.println(result);
}
Фактический вывод:
79792266297612000
Обратите внимание на последнюю цифру: вместо 79792266297612001 получаем 79792266297612000 — расхождение на единицу. Это не баг конкретной реализации, а следствие того, что double физически не может хранить каждое целое число за пределами 2^53 с абсолютной точностью, а Math.pow (как и большинство реализаций pow в других языках) не обязана давать результат, округленный до последнего бита правильно, для любых входных данных.
Практический вывод: Math.pow годится для дробных степеней и для целых чисел, где итог заведомо меньше 2^53. Если нужна гарантированная точность на больших целых числах — берите ручной цикл на целых типах или BigInteger, где нет плавающей точки вообще.
Ручной цикл: точный результат для целых чисел
Цикл умножения не использует double и поэтому не теряет точность, пока итоговое значение помещается в выбранный целочисленный тип:
public static long powLoop(long base, int exp) {
long result = 1;
for (int i = 0; i < exp; i++) {
result *= base;
}
return result;
}
public static void main(String[] args) {
System.out.println(powLoop(7, 20));
}
Вывод:
79792266297612001
Здесь число совпадает с точным значением — никакой погрешности, потому что вся арифметика целочисленная. Обратная сторона — при отрицательном основании и целочисленном делении такой цикл не умеет считать дробные и отрицательные степени, для этого все равно нужен Math.pow или отдельная ветка кода.
Для больших показателей есть более быстрый вариант того же принципа — возведение в степень удвоением (aka быстрое/бинарное возведение в степень), которое считает результат за O(log n) умножений вместо O(n):
public static long fastPow(long base, int exp) {
if (exp == 0) {
return 1;
}
long half = fastPow(base, exp / 2);
if (exp % 2 == 0) {
return half * half;
} else {
return half * half * base;
}
}
fastPow(2, 10) дает 1024 всего за 4 рекурсивных вызова вместо 10 итераций обычного цикла — разница малозаметна на маленьких числах, но существенна, когда показатель степени исчисляется тысячами (например, в модульной арифметике для криптографии, где обычно считают fastPow по модулю на каждом шаге).
BigInteger.pow: когда результат не помещается в long
long тоже не бесконечен — его максимум 9223372036854775807. Если показатель степени большой, а тип BigInteger работает с числами произвольной длины (ограниченными только памятью), возьмем 3 в 40-й степени, точное значение которой — 12157665459056928801. Это уже больше Long.MAX_VALUE, поэтому ни long-цикл, ни Math.pow с приведением к long не дадут верного ответа.
import java.math.BigInteger;
public class BigPowExample {
public static void main(String[] args) {
BigInteger base = BigInteger.valueOf(3);
BigInteger result = base.pow(40);
System.out.println(result);
}
}
Вывод:
12157665459056928801
Значение точное — BigInteger хранит число как массив цифр произвольной длины, а не как биты фиксированного размера, поэтому границ int/long для него не существует. Метод pow принимает показатель степени только типом int (отрицательные показатели он не принимает — для дробных степеней BigInteger не подходит вовсе, там нужен Math.pow или BigDecimal с приближенными алгоритмами).
Побитовый сдвиг: самый быстрый способ для степеней двойки
Если основание — именно число 2, возведение в степень сводится к сдвигу бит влево: 1 << n эквивалентно 2^n. Это самая быстрая из всех операций — один такт процессора, без циклов и без вызовов методов.
public static void main(String[] args) {
System.out.println(1 << 10);
System.out.println(Math.pow(2, 10) == (1 << 10));
}
Вывод:
1024
true
У способа есть строгая граница применимости — он работает только для основания 2 и только в пределах ширины типа. Для int (32 бита, один бит уходит под знак) безопасный диапазон показателя степени — от 0 до 30: 1 << 31 дает не 2147483648, а -2147483648 (Integer.MIN_VALUE), потому что 31-й бит — знаковый. Для long (64 бита) граница сдвигается до 62, а 1L << 63 аналогично дает Long.MIN_VALUE. Кроме того, оператор сдвига в Java берет только младшие биты правого операнда (5 бит для int, 6 бит для long), поэтому 1 << 32 в Java равно не 0, а 1 << 0, то есть 1 — неочевидное поведение, если ожидать сдвиг «на ноль позиций эффективно занулит число».
Какой способ выбрать
| Способ | Тип результата | Точность на больших числах | Когда использовать |
|---|---|---|---|
Math.pow |
double |
теряется за пределами 2^53 (9007199254740992) | дробная или отрицательная степень, точность не критична |
| Ручной цикл | long |
точна, пока итог <= Long.MAX_VALUE | целая степень, важна точность, без внешних классов |
BigInteger.pow |
BigInteger |
точна всегда (ограничена только памятью) | результат не помещается в long: факториалы, криптография |
Побитовый сдвиг << |
int/long |
точна в пределах ширины типа | только основание 2, максимальная скорость |
Выводы
Math.powвозвращаетdoubleи подходит для дробных/отрицательных степеней; для целых чисел годится, только если результат заведомо меньше 2^53.- Приведение
(int) Math.pow(...)при выходе за границыintне бросает исключение, а насыщается доInteger.MAX_VALUE/MIN_VALUE— это ловушка, а не переполнение в привычном смысле. - Ручной цикл на
longдает точный целочисленный результат, пока итог помещается вlong; для больших показателей есть более быстрый вариант — возведение удвоением за O(log n). BigInteger.pow— единственный точный способ, когда результат превышаетLong.MAX_VALUE.- Побитовый сдвиг
<<— самый быстрый вариант, но работает только для основания 2 и в пределах ширины типа (до 30 дляint, до 62 дляlong).
Где применяется / связь с практикой
Освойте тему на практике
Работа с числовыми типами, границами точности и приведением типов — база, которая нужна в любой Java-задаче: от школьных алгоритмов до продакшен-кода, где ошибка вроде тихого насыщения int при приведении из double может неделями жить в коде и портить отчеты или расчеты. На курсе Java Basic разбирают типы данных, работу с числами и типичные ошибки приведения на практических задачах с разбором кода. Если хочется сначала посмотреть на формат занятий — можно записаться на открытые уроки Otus.
FAQ
Есть ли в Java оператор возведения в степень, как ** в Python?
Нет, в Java такого оператора нет ни для int, ни для double. Единственный встроенный инструмент — статический метод Math.pow, остальные способы (цикл, BigInteger, сдвиг) пишутся вручную или берутся из стандартной библиотеки классов.
Можно ли возвести число в отрицательную степень?
Да, но только через Math.pow — он возвращает дробный double (например, Math.pow(2, -1) дает 0.5). Ручной цикл на целых типах и BigInteger.pow из примеров выше рассчитаны только на неотрицательные целые показатели степени.
Что вернет возведение числа в нулевую степень?
Math.pow(x, 0) возвращает 1.0 для любого x, кроме случая с NaN в основании. Ручной цикл из примера выше тоже корректно вернет 1, потому что просто не войдет в тело цикла ни разу и оставит стартовое значение result = 1.



