Ускоряем pow
В этой статье я хочу поделиться несколькими нестандартными алгоритмами для быстрого возведения числа в степень, а также продемонстрировать их реализацию и сравнить их быстродействие в C++, C# и Java.

Сравнить точность алгоритмов можно прямо сейчас на этой странице.
В конце будет краткая памятка по тому, где и когда лучше применять какой из методов. При правильном выборе можно добиться увеличения скорости вычислений в 5 раз при погрешности
1%, а иногда и вовсе без неё.
Содержание
Алгоритмы (5 штук)
На повестке дня у нас есть 5 алгоритмов: «Старая аппроксимация», «Бинарная степень», «Делящая быстрая степень», «Дробная” быстрая степень» и «Другая” аппроксимация».
Названия алгоритмам я придумал сам (за исключением бинарной степени), так как нигде не нашёл официальных версий, но вы можете называть их иначе.
Для расчета прироста скорости и погрешности будем сравнивать эти методы со стандартными функциями pow, Math.Pow и Math.pow в C++, C# и Java соответственно. О том, как производилось сравнение, будет сказано в частях “Сравнение производительности” и “Сравнение точности”.
Алгоритм: «Старая аппроксимация»
Увеличение скорости: в
11 раз
Погрешность: <2%
Ограничения: приемлемая точность только для степеней от -1 до 1
Реализация в C# и Java
Этот метод основан на алгоритме, использованном в игре Quake III Arena 2005 года. Он возводил число x в степень -0.5, т.е. находил значение:
Разработчики для этого написали такую функцию
Узнал я об этом методе из статьи «Магическая константа» 0x5f3759df. В ней подробно объясняется как работает этот код и как его можно улучшить для работы с любой степенью и double’ми вместо float’ов. В моих кодах также есть магическая константа 4606853616395542500L. Нашёл я её по следующей формуле (она описана в статье выше):
Число 1.0730088 было подобрано вручную для достижения наибольшей точности вычислений.
Алгоритм: Бинарное возведение в степень
Увеличение скорости: в среднем в
7.5 раз, преимущество сохраняется до возведения чисел в степень 134217728 в C++/C# и 4096 в Java.
Погрешность: нет, но стоит отметить, что операция умножения не ассоциативна для чисел с плавающей точкой, т.е. 1.21 * 1.21 не то же самое, что 1.1 * 1.1 * 1.1 * 1.1, однако при сравнении со стандартными функциями погрешности, как уже сказано ранее, не возникает.
Ограничения: степень должна быть целым числом не меньше 0
Реализация в C# и Java
Широко известный алгоритм для возведения любого числа в целую степень с абсолютной точностью. Принцип действия прост: есть целая степень e, чтобы получить число b в этой степени нужно возвести это число во все степени 1, 2, 4, … 2 n (в коде этому соответствует b *= b), каждый раз сдвигая биты e вправо (e >>= 1) пока оно не равно 0 и тогда, когда последний бит e не равен нулю ((e & 1) != 0), домножать результат v на полученное b.
Пример: возвести 2 в степень 5.
v = 1, e = 5 = 1012, b = 2
Шаги цикла:
e = 1012 — последний 1 → v *= b → v = 2
b *= b → b = 4
e >>= 1 → e = 102 = 2
e = 102 — последний 0 → пропускаем
b *= b → b = 16
e >>= 1 → e = 1
e = 12 — последний 1 → v *= b → v = 32
.
e = 0 → выход из цикла
Результат: v = 32, что и есть 2 5 .
Алгоритм: «Делящая быстрая степень»
Увеличение скорости: в
13%
Примечание: в коде ниже присутствуют проверки для особых входных данных. Без них код работает всего на 10% быстрее, но погрешность возрастает в десятки раз (особенно при использовании отрицательных степеней).
Реализация в C# и Java
Узнав о методе аппроксимации чисел в степенях от -1 до 1 и о бинарном методе, мне захотелось объединить их для создания функции, которая могла бы быстро возводить число в любую степень. Для этого я придумал следующую формулу:
Мы разбиваем степень на две части: e / el, которая всегда меньше или равна 1, и el, которая является целым числом. Теперь для расчета x^(e / el) мы можем использовать “старую” аппроксимацию, а для x^el — бинарную степень.Таким образом, объединяя этих два узкоспециализированных метода, мы получили универсальный метод. Но эту идею можно реализовать по-другому.
Алгоритм: «Дробная быстрая степень»
Увеличение скорости: в
Реализация в C# и Java
По сути, любое число состоит из суммы двух частей: целой и дробной. Целую можно использовать для возведения основания в степень при помощи бинарного возведения, а дробную — при помощи “старой” аппроксимации.
В результате получаем следующую формулу:
Она, в отличии от формулы “делящего” метода, никак не искажает дробную часть. Это позволяет добиться намного большей точности.
Алгоритм: «Другая аппроксимация»
Увеличение скорости: в
9 раз
Погрешность: <1.5%
Ограничения: точность стремительно падает при повышении абсолютного значения степени и остается приемлемой в промежутке [-10, 10]
Реализация в C# и Java
Про историю этого алгоритма я ничего не знаю, я просто нашёл его тут: Optimized pow() approximation for Java, C / C++, and C#. Возможно, если использовать его в “делящей–” и “дробной быстрых степенях» вместо “старой” аппроксимации, можно достигнуть лучшей точности ценой немного меньшей скорости.
Сравнение производительности
Сравнение производительности производилось следующим образом: генерируем 500000 чисел-оснований в промежутке от 0.0 до 99999.0 и 500000 чисел-степеней в промежутке от A до B. Запоминаем текущее время, запускаем цикл на 500000 итераций, вычисляем значение основания в степени через функцию f и результат суммируем в calculationResult. По окончанию цикла снова замеряем время, разница во времени и есть время выполнения. Данная процедура повторяется 20 раз, конечный результат — усредненный за все 20 тестов.
Псевдокод сравнения производительности в C++:
Аналогично измерялась скорость в C# и Java. В репозитории проекта можно посмотреть на реальный код для сравнения производительности в C++, C# и Java.
Тесты производились в каждом языке для степеней в промежутках [-10.5, 0], [0, 2], [0, 10.5], [0, 25.75], [0, 55.5]. Прирост скорости каждого метода по сравнению со стандартным в каждом языке для каждого сета степеней изображен на графиках ниже:

Рассмотреть подробнее результаты тестов можно посмотреть в этой таблице.
Тесты проводились на i5-10300H, 19.8 DDR4 GB usable RAM, 64-битная платформа.
C++: MSVC + /O2 + /Oi + /Ot
C#: optimize code
Сравнение точности
Для рассчитывания точности я возводил очередное число в определенную степень стандартным и одним из нестандартных способами, потом делил большее из полученных чисел на меньшее, складывал все эти отношения вместе, а в конце делил их на количество сравнений. Так и получалось значение погрешности.
Основания и степени генерировались также, как и в алгоритме сравнения производительности.
Для более гибкого и удобного тестирования я создал Blazor страницу, на которой можно построить графики реальных и полученных одним из методов значений чисел, лежащих в заданном промежутке и возведенных в указанную степень:

Ниже на этой же странице можно самим провести сравнение точности каждого метода для всех степеней, лежащих в заданном промежутке:

Вывод
Каждый из перечисленных мною методов дает различную точность и скорость. Поэтому, прежде чем использовать стандартную функцию возведения в степень, стоит проанализировать какие у вас будут входные данные, и насколько вам важна точность. Выбор правильного метода может существенно ускорить работу вашей программы. Чтобы сделать этот процесс проще, я подготовил вот такую шпаргалку:

Код всего проекта доступен на гитхабе. Там вы найдете и реализации всех алгоритмов в трех языках, и программы для сравнения точности для каждого языка, и код сайта.
Спасибо за внимание. Надеюсь эта статья поможет сделать ваш код немного быстрее.
Возведение в степень: правила, примеры
Мы разобрались, что вообще из себя представляет степень числа. Теперь нам надо понять, как правильно выполнять ее вычисление, т.е. возводить числа в степень. В этом материале мы разберем основные правила вычисления степени в случае целого, натурального, дробного, рационального и иррационального показателя. Все определения будут проиллюстрированы примерами.
Понятие возведения в степень
Начнем с формулирования базовых определений.
Возведение в степень — это вычисление значения степени некоторого числа.
То есть слова «вычисление значение степени» и «возведение в степень» означают одно и то же. Так, если в задаче стоит «Возведите число 0 , 5 в пятую степень», это следует понимать как «вычислите значение степени ( 0 , 5 ) 5 .
Теперь приведем основные правила, которым нужно придерживаться при таких вычислениях.
Как возвести число в натуральную степень
Вспомним, что такое степень числа с натуральным показателем. Для степени с основанием a и показателем n это будет произведение n -ного числа множителей, каждый из которых равен a . Это можно записать так:

Чтобы вычислить значение степени, нужно выполнить действие умножения, то есть перемножить основания степени указанное число раз. На умении быстро умножать и основано само понятие степени с натуральным показателем. Приведем примеры.
Условие: возведите — 2 в степень 4 .
Решение
Используя определение выше, запишем: ( − 2 ) 4 = ( − 2 ) · ( − 2 ) · ( − 2 ) · ( − 2 ) . Далее нам нужно просто выполнить указанные действия и получить 16 .
Возьмем пример посложнее.
Вычислите значение 3 2 7 2
Решение
Данную запись можно переписать в виде 3 2 7 · 3 2 7 . Ранее мы рассматривали, как правильно умножать смешанные числа, упомянутые в условии.
Выполним эти действия и получим ответ: 3 2 7 · 3 2 7 = 23 7 · 23 7 = 529 49 = 10 39 49
Если в задаче указана необходимость возводить иррациональные числа в натуральную степень, нам потребуется предварительно округлить их основания до разряда, который позволит нам получить ответ нужной точности. Разберем пример.
Выполните возведение в квадрат числа π .
Решение
Для начала округлим его до сотых. Тогда π 2 ≈ ( 3 , 14 ) 2 = 9 , 8596 . Если же π ≈ 3 . 14159 , то мы получим более точный результат: π 2 ≈ ( 3 , 14159 ) 2 = 9 , 8695877281 .
Отметим, что необходимость высчитывать степени иррациональных чисел на практике возникает сравнительно редко. Мы можем тогда записать ответ в виде самой степени ( ln 6 ) 3 или преобразовать, если это возможно: 5 7 = 125 5 .
Отдельно следует указать, что такое первая степень числа. Тут можно просто запомнить, что любое число, возведенное в первую степень, останется самим собой:

Это понятно из записи .
От основания степени это не зависит.
Так, ( − 9 ) 1 = − 9 , а 7 3 , возведенное в первую степень, останется равно 7 3 .
Как возвести число в целую степень
Для удобства разберем отдельно три случая: если показатель степени — целое положительное число, если это ноль и если это целое отрицательное число.
В первое случае это то же самое, что и возведение в натуральную степень: ведь целые положительные числа принадлежат ко множеству натуральных. О том, как работать с такими степенями, мы уже рассказали выше.
Теперь посмотрим, как правильно возводить в нулевую степень. При основании, которое отличается от нуля, это вычисление всегда дает на выходе 1 . Ранее мы уже поясняли, что 0 -я степень a может быть определена для любого действительного числа, не равного 0 , и a 0 = 1 .
5 0 = 1 , ( — 2 , 56 ) 0 = 1 2 3 0 = 1
0 0 — не определен.
У нас остался только случай степени с целым отрицательным показателем. Мы уже разбирали, что такие степени можно записать в виде дроби 1 a z , где а — любое число, а z — целый отрицательный показатель. Мы видим, что знаменатель этой дроби есть не что иное, как обыкновенная степень с целым положительным показателем, а ее вычислять мы уже научились. Приведем примеры задач.
Возведите 2 в степень — 3 .
Решение
Используя определение выше, запишем: 2 — 3 = 1 2 3
Подсчитаем знаменатель этой дроби и получим 8 : 2 3 = 2 · 2 · 2 = 8 .
Тогда ответ таков: 2 — 3 = 1 2 3 = 1 8
Возведите 1 , 43 в степень — 2 .
Решение
Переформулируем: 1 , 43 — 2 = 1 ( 1 , 43 ) 2
Вычисляем квадрат в знаменателе: 1,43·1,43. Десятичные дроби можно умножить таким способом:
В итоге у нас вышло ( 1 , 43 ) — 2 = 1 ( 1 , 43 ) 2 = 1 2 , 0449 . Этот результат нам осталось записать в виде обыкновенной дроби, для чего необходимо умножить ее на 10 тысяч (см. материал о преобразовании дробей).
Ответ: ( 1 , 43 ) — 2 = 10000 20449
Отдельный случай — возведение числа в минус первую степень. Значение такой степени равно числу, обратному исходному значению основания: a — 1 = 1 a 1 = 1 a .
Пример: 3 − 1 = 1 / 3
9 13 — 1 = 13 9 6 4 — 1 = 1 6 4 .
Как возвести число в дробную степень
Для выполнения такой операции нам потребуется вспомнить базовое определение степени с дробным показателем: a m n = a m n при любом положительном a , целом m и натуральном n .
Таким образом, вычисление дробной степени нужно выполнять в два действия: возведение в целую степень и нахождение корня n -ной степени.
У нас есть равенство a m n = a m n , которое, учитывая свойства корней, обычно применяется для решения задач в виде a m n = a n m . Это значит, что если мы возводим число a в дробную степень m / n , то сначала мы извлекаем корень n -ной степени из а , потом возводим результат в степень с целым показателем m .
Проиллюстрируем на примере.
Вычислите 8 — 2 3 .
Решение
Способ 1. Согласно основному определению, мы можем представить это в виде: 8 — 2 3 = 8 — 2 3
Теперь подсчитаем степень под корнем и извлечем корень третьей степени из результата: 8 — 2 3 = 1 64 3 = 1 3 3 64 3 = 1 3 3 4 3 3 = 1 4
Способ 2. Преобразуем основное равенство: 8 — 2 3 = 8 — 2 3 = 8 3 — 2
После этого извлечем корень 8 3 — 2 = 2 3 3 — 2 = 2 — 2 и результат возведем в квадрат: 2 — 2 = 1 2 2 = 1 4
Видим, что решения идентичны. Можно пользоваться любым понравившимся способом.
Бывают случаи, когда степень имеет показатель, выраженный смешанным числом или десятичной дробью. Для простоты вычислений его лучше заменить обычной дробью и считать, как указано выше.
Возведите 44 , 89 в степень 2 , 5 .
Решение
Преобразуем значение показателя в обыкновенную дробь: 44 , 89 2 , 5 = 44 , 89 5 2 .
А теперь выполняем по порядку все действия, указанные выше: 44 , 89 5 2 = 44 , 89 5 = 44 , 89 5 = 4489 100 5 = 4489 100 5 = 67 2 10 2 5 = 67 10 5 = = 1350125107 100000 = 13 501 , 25107
Ответ: 13 501 , 25107 .
Если в числителе и знаменателе дробного показателя степени стоят большие числа, то вычисление таких степеней с рациональными показателями — довольно сложная работа. Для нее обычно требуется вычислительная техника.
Отдельно остановимся на степени с нулевым основанием и дробным показателем. Выражению вида 0 m n можно придать такой смысл: если m n > 0 , то 0 m n = 0 m n = 0 ; если m n < 0 нуль остается не определен. Таким образом, возведение нуля в дробную положительную степень приводит к нулю: 0 7 12 = 0 , 0 3 2 5 = 0 , 0 0 , 024 = 0 , а в целую отрицательную — значения не имеет: 0 — 4 3 .
Как возвести число в иррациональную степень
Необходимость вычислить значение степени, в показателе которой стоит иррациональное число, возникает не так часто. На практике обычно задача ограничивается вычислением приблизительного значения (до некоторого количества знаков после запятой). Обычно это считают на компьютере из-за сложности таких подсчетов, поэтому подробно останавливаться на этом не будем, укажем лишь основные положения.
Если нам нужно вычислить значение степени a с иррациональным показателем a , то мы берем десятичное приближение показателя и считаем по нему. Результат и будет приближенным ответом. Чем точнее взятое десятичное приближение, тем точнее ответ. Покажем на примере:
Вычислите приближенное значение 2 в степени 1,174367.
Решение
Ограничимся десятичным приближением a n = 1 , 17 . Проведем вычисления с использованием этого числа: 2 1 , 17 ≈ 2 , 250116 . Если же взять, к примеру, приближение a n = 1 , 1743 , то ответ будет чуть точнее: 2 1 , 174367 . . . ≈ 2 1 , 1743 ≈ 2 , 256833 .
Быстрое возведение в степень.
Для возведения числа x в степень n, как правило, используют стандартный метод, т. е. число x умножают n раз само на себя. В задачах математического толка, решаемых при помощи бумаги и ручки, такой метод вполне приемлем, ведь степенная функция быстро растет и поэтому сомнительно, что придется производить затруднительные операции вручную.
Другое дело программирование, где важно не только решить поставленную задачу, но и составить оптимальное решение, удовлетворяющее предусмотренному диапазону входных данных. Так, в частности, для операции возведения числа в степень имеется алгоритм, позволяющий значительно сократить число требуемых операций. Он достаточно прост и основывается на математических свойствах степеней.
Пусть имеется некоторая степень x n , где x – действительное число, а n – натуральное. Тогда для x n справедливо равенство:
x n = (x m ) k
При этом m*k=n. Например: 3 6 =(3 3 ) 2 , 5 7 =(5 7 ) 1 . Это свойство является одним из основных степенных свойств, и именно на нем основывается рассматриваемый метод. Далее, заметим, что в случае, если n является четным числом, то верно следующее равенство:
x n = (x n/2 ) 2 = x n/2 * x n/2
Так, если x=3, а n=6, то имеем 3 6 = (3 6/2 ) 2 = 3 6/2 * 3 6/2 . Используя это свойство, удастся существенно уменьшить число операций необходимых для возведения x в степень n. Теперь адаптируем формулу для случая с нечетным n. Для этого понадобиться просто перейти к степени на единицу меньшей. Например: 5 7 = 5 6 *5, 12 5 = 12 4 *12. Общая форма равенства перехода:
x n = x n-1 *x
В программе, реализующей алгоритм быстрого возведения в степень, используются указанные свойства: если степень n четная, то переходим к степени вдвое меньшей, иначе заменяем по имеющимся правилам нечетную степень на четную.