9

In C++ 03, using e.g. std::pow(double_val, 6) was considerably faster than using std::pow(double_val, 6.0).

This is no longer the case when compiling with C++11. Looking at the cmath header from gcc's libstdc++-4.8, one can see an explicit pow(double, int) is no longer present, this case is handled by the following template which promotes the int to a double:

template<typename _Tp, typename _Up>
inline typename __gnu_cxx::__promote_2<_Tp, _Up>::__type
pow(_Tp __x, _Up __y) {
  typedef typename __gnu_cxx::__promote_2<_Tp, _Up>::__type __type;
  return std::pow(__type(__x), __type(__y));
}

Is this behavior in the C++11 standard or could future libstdc++ implementations return to the faster method?

Secondly, if for standard or implementation reasons the faster behavior is no longer possible, what is the most portable way to achieve it again? I see a power(...) function in the gcc stdlibc++ under "ext/numeric", but this is marked as a non-standard SGI(rip) extension.

toting
  • 629
  • 3
  • 14
  • [Sounds right](http://en.cppreference.com/w/cpp/numeric/math/pow): "If any argument has integral type, it is cast to `double`". Weird. And I can't find the reference in the standard... – Kerrek SB Sep 18 '13 at 13:12
  • @KerrekSB: I wonder what *effectively cast* means. Does that inhibit the implementation from offering other overloads? From a math point of view `x^5` and `x^5.0` are the same thing...(although they might not be in an implementation). I am not sure that the standard mandates that no extra overloads are provided if the results of both are the same. – David Rodríguez - dribeas Sep 18 '13 at 13:20
  • 1
    @DavidRodríguez-dribeas: I don't even know where the website takes that from. Can't find a reference for that... – Kerrek SB Sep 18 '13 at 13:29
  • @KerrekSB see [c.math]/3, and prepare c standard. Then it just add two new pow signatures that do not exists in c – BЈовић Sep 18 '13 at 13:39
  • 2
    related question: [Why was std::pow(double, int) removed from C++11?](http://stackoverflow.com/questions/5627030/why-was-stdpowdouble-int-removed-from-c11) – Hulk Sep 18 '13 at 13:46
  • 2
    @KerrekSB There is, in **26.8 paragraph 11 [c.math]**. Had [some problems](http://stackoverflow.com/q/14295217/743214) with that myself already. – Christian Rau Sep 18 '13 at 13:57

2 Answers2

4

First of all, yes this behaviour is consistent with the C++11 standard (though not with C++03), which in section 26.8, paragraph 11 says:

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any argument corresponding to a double parameter has type long double, then all arguments corresponding to double parameters are effectively cast to long double.

  2. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.

  3. Otherwise, all arguments corresponding to double parameters are effectively cast to float.

(In addition to the overloads for float-only, double-only and long double-only.)

So the implementation actually has to cast that integer argument into a double and I don't think there is a possibility for a conforming library to provide a faster std::pow for integer powers, apart from maybe checking the double argument for integrality (is that a word?) and using a special path in this case.

In order to provide a platform-independent faster way, the only thing that comes to my mind would be to write a custom wrapper that delegates to this non-standard power if it is present. Other than that I don't know how you could infuse that behaviour into std::pow again without writing your own implementation.

EDIT: Yet when looking at this answer it is indeed possible for an implementation to still provide an optimized overload for integer powers as long as it behaves exactly like std::pow(double(x), double(y)). So there is a possiblity for an implementation to provide that faster version, yet I wouldn't count so much on this as you have done in C++03 (where it was IMHO even part of the standard, but I might be wrong).

Community
  • 1
  • 1
Christian Rau
  • 45,360
  • 10
  • 108
  • 185
2

C provides just the double pow(double, double) overload (recall that C doesn't allow function overloading anyway). The C++11 standard (26.8/9) says that the following overloads are added (in addition to C's) to namespace std:

float pow(float, float);
long double pow(long double, long double);

Hence, double pow(double, int) is an extension rather than a standard function. Therefore an implementation might provide it or not.

Edit After Christian Rau comment and answer.

Now I believe the overload must be there. Hence, there should be a double pow(double, int) overload. However, this overload must (as he says in his answer) cast the int to a double and call double pow(double, double).

Actually there are a lot of overloads. The easiest way to provide them is not as normal functions but as a template function exactly as the libstdc++ implementation shown in the OP.

See also this open issue where Howard Hinnant explains the details.

Finally the link provided by Hulk is very relevant and Howard Hinnant (again) explains that there's almost no reason to provide an optimized overload that does anything different from casting the intto the double.

Community
  • 1
  • 1
Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
  • you also need to see which pow signatures are available. `double pow(double, double)` may no be an extension – BЈовић Sep 18 '13 at 13:41
  • @Hulk: The questions were: (1) "Is this behavior in the C++11 standard" (my answer is "yes"); (2) "could future libstdc++ implementations return to the faster method?" (my answer is "yes they can but that's not required)"; and (3) "what is the most portable way to achieve it again?" (I didn't answer because I don't know). Why it has been removed is another question that wasn't asked and for which I don't have an answer. – Cassio Neri Sep 18 '13 at 13:45
  • @BЈовић: `double pow(double, double)` is part of the C library that is refered in C++11 standard. It's declared in header `` (see C++11 26.8/3, table 119). In addition, 26.8/8 says "In addition to the `double` versions of the math functions in ``, C++ adds `float` and `long double` overloaded versions of these functions". – Cassio Neri Sep 18 '13 at 13:49
  • @CassioNeri You are right, I misread the question. – Hulk Sep 18 '13 at 13:49
  • That isn't completely true. In 26.8 paragraph 11 the C++11 standard demands additional overloads, exactly to convert any integer into `double`. – Christian Rau Sep 18 '13 at 14:01
  • @ChristianRau: I think you are right in the sense that the overload exists. However, I agree with Chistian Rau when he says (see his answer) that the implementation has to cast the `int` to a `double`. Hence, I don't see the point of the overload: Wouldn't an automatic promotion `int` -> `double` do the same? – Cassio Neri Sep 18 '13 at 14:12
  • @CassioNeri *"Wouldn't an automatic promotion int -> double do the same?"* - Not neccessarily. What happens for `std::pow(1.0f, 1)`, it would take the `float`-version whereas it should take the `double`-version. And even worse, `std::pow(1, 1)` would be ambiguous. – Christian Rau Sep 18 '13 at 14:16
  • @ChristianRau: First of all: I apologise for not realizing that you were the same person aswering the question and commenting on my post. Second. I agree with you: other calls (which are not `pow(double, int)`) would have to be treated sligth differently and reliying on automatic promotion wouldn't be enough. I've updated my answer and provided a link that might be useful. Thanks. – Cassio Neri Sep 18 '13 at 14:33
  • @CassioNeri Yet I updated my answer, since *Howard* stated in another answer of his, that it would still be possible to treat `pow(double, int)` differently, as long as it behaves *like* `pow(double, double(int))`. By the way, your link directs to the top of the *huge* issue list. Was it maybe [2086](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2086) you were referring to? – Christian Rau Sep 18 '13 at 14:37
  • @ChristianRau: Oh dear! I've fixed the link but it's not the one you thought. – Cassio Neri Sep 18 '13 at 14:40