Почему std::pow(double, int) был удален из C++11?

Изучая Эффективный способ вычисления p^q (возведение в степень), где q — целое число, и рассматривая C++98 и C Стандарты ++11 Я заметил, что, по-видимому, перегрузка std::pow(double, int) была удалена в C++11.

В C++98 26.5/6 он имеет подпись double pow(double, int);.

В C++11 26.8 все, что я мог найти, это перегрузки, принимающие пару float, double или long double, и явное примечание о том, что в случае смеси типов параметров integer&double следует выбрать перегрузку pow(double, double).

Является ли это просто разъяснением предыдущего намерения, были ли они неправильно добавлены в C++98, действительно ли они были удалены в C++11 или что-то еще?

Очевидно, что версия pow(double, int) предоставляет хорошие возможности для оптимизации, поэтому кажется странным, что они будут удалены. Будет ли компилятор по-прежнему соответствовать стандартам для обеспечения такой оптимизированной перегрузки?


person Mark B    schedule 11.04.2011    source источник
comment
Прекрасная возможность для оптимизации — это реализация bignum, а не double, которые, скорее всего, поддерживаются аппаратно и где также можно использовать решение log+mul+exp.   -  person 6502    schedule 12.04.2011
comment
@Ben Voigt: C++03 соответствует C++98 в этом.   -  person Jerry Coffin    schedule 12.04.2011


Ответы (1)


double pow(double, int);

не был удален из спецификации. Его просто переформулировали. Теперь он находится в [c.math]/p11. Как это вычисляется, является деталью реализации. Единственная измененная сигнатура C++03:

float pow(float, int);

Теперь это возвращает двойной:

double pow(float, int);

И это изменение было сделано для совместимости с C.

Пояснение:

26.8 [cmath] / p11 говорит:

При этом должны быть предусмотрены дополнительные перегрузки, достаточные для обеспечения:

  1. Если какой-либо аргумент, соответствующий параметру double, имеет тип long double, то все аргументы, соответствующие параметрам double, эффективно преобразуются в тип long double.

  2. В противном случае, если какой-либо аргумент, соответствующий параметру double, имеет тип double или целочисленный тип, то все аргументы, соответствующие параметрам double, фактически преобразуются в тип double.

  3. В противном случае все аргументы, соответствующие параметрам типа double, фактически преобразуются в число с плавающей запятой.

Этот пункт подразумевает целую массу перегрузок, среди которых:

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) в отдельный алгоритм, большинство разработчиков, которых я знаю, отказались от этой стратегии, за исключением, возможно, проверки очень малых целочисленных показателей. И в этом случае обычно выгодно поместить эту проверку в реализацию с экспонентой с плавающей запятой, чтобы получить максимальную отдачу от ваших затрат на оптимизацию.

person Howard Hinnant    schedule 11.04.2011
comment
Является ли это изменением еще неопубликованной окончательной версии? - person Bo Persson; 12.04.2011
comment
Вы говорите, что pow(double, int); не был удален, но мой компилятор, кажется, так думает, как мне на самом деле получить к нему доступ? - person crobar; 24.01.2014
comment
Если ваша реализация соответствует C++11, вы #include ‹cmath› и вызываете std::pow(1.0, 1). - person Howard Hinnant; 24.01.2014
comment
@crobar: Чтобы доказать, что на самом деле существует pow(double, int), попробуйте double (*fn)(double, int) = std::pow; Если бы компилятор нашел pow(double, double), то std::pow(1.0, 1) Говарда завершился бы успешно, но мой код указателя на функцию завершился бы ошибкой. - person Ben Voigt; 22.12.2015