Ответ 1
double pow(double, int);
не был удален из спецификации. Это просто переформулировано. Сейчас он живет в [c.math]/p11. Как он вычисляется, является детальностью реализации. Единственная сигнатура С++ 03, которая изменилась:
float pow(float, int);
Теперь это возвращает double:
double pow(float, int);
И это изменение было выполнено для совместимости C.
Разъяснение
26.8 [cmath]/p11 говорит:
Кроме того, должны быть дополнительные перегрузок, достаточных для обеспечения:
Если любой аргумент, соответствующий двойному параметру, имеет тип long double, то все аргументы, соответствующие эффективно применяются двойные параметры к длинному двойному.
В противном случае, если любой аргумент, соответствующий двойному параметру имеет тип double или целочисленный тип, то все аргументы, соответствующие эффективно применяются двойные параметры удвоить.
В противном случае все аргументы, соответствующие двойным параметрам, эффективно бросать в float.
Этот параграф подразумевает целый ряд перегрузок, в том числе:
double pow(double, int);
double pow(double, unsigned);
double pow(double, unsigned long long);
и др.
Это могут быть фактические перегрузки или могут быть реализованы с ограниченными шаблонами. Я лично реализовал его в обоих направлениях и решительно поддерживал реализацию ограниченного шаблона.
Второе обновление для проблем оптимизации адресов:
Реализация позволяет оптимизировать любую перегрузку. Но помните, что оптимизация должна быть только такой. Оптимизированная версия должна возвращать тот же ответ. Опыт от разработчиков таких функций, как pow, заключается в том, что к тому моменту, когда вы столкнетесь с проблемой, чтобы ваша реализация с интегральным показателем давала тот же ответ, что и реализация с показателем с плавающей запятой, "оптимизация" часто медленнее.
В качестве демонстрации следующая программа дважды печатает pow(.1, 20)
, используя std:: pow, и второй раз, используя "оптимизированный" алгоритм, используя интегральный показатель:
#include <cmath>
#include <iostream>
#include <iomanip>
int main()
{
std::cout << std::setprecision(17) << std::pow(.1, 20) << '\n';
double x = .1;
double x2 = x * x;
double x4 = x2 * x2;
double x8 = x4 * x4;
double x16 = x8 * x8;
double x20 = x16 * x4;
std::cout << x20 << '\n';
}
В моей системе это выдает:
1.0000000000000011e-20
1.0000000000000022e-20
Или в шестнадцатеричной нотации:
0x1.79ca10c92422bp-67
0x1.79ca10c924232p-67
И да, разработчики pow действительно беспокоятся обо всех этих битах в нижнем конце.
Таким образом, хотя свобода заключается в перетасовке pow(double, int)
на отдельный алгоритм, большинство разработчиков, о которых я знаю, отказались от этой стратегии, за исключением, возможно, проверки очень малых интегральных показателей. И в этом случае обычно выгодно поставить эту проверку в реализацию с показателем с плавающей запятой, чтобы получить самый большой взлом для вашей оптимизации.