7

I have been programming in C++ for a while now. I have seen previously that power function gives wrong answer for bigger powers due to precision issues but today while solving coding problems I saw that under the same type of parameters, pow() function gave different values when put inside a function vs when evaluated directly.

 #include <iostream>
 #include <math.h>
 using namespace std;

 long long n,d;

 long long power(long long x)
 {
    return pow(100,x);
 }

 long long powersecond(long long x)
 {
    return pow(100,(int)x);
 }

 int main()
 {
    n = 68; d = 2;
    cout << n*power(d) <<endl;        // outputs 679932
    cout << n*pow(100,d) <<endl;      // outputs 680000
    cout << n*powersecond(d) <<endl;  // outputs 679932
    cout << n*pow(100,(int)d) <<endl; // outputs 680000
    return 0;
 }

Notice that the answer doesn't change even after converting x to integer in powersecond() function.The answer is still 679932 even if d is int instead of long long int. The compiler I used is gnu gcc compiler in VS Code.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 2
    No need for macros: `using ll = long long;` – keith May 21 '20 at 19:31
  • 3
    There is no issue, each is resolving to a call to [std::pow](https://en.cppreference.com/w/cpp/numeric/math/pow) with different types. – keith May 21 '20 at 19:35
  • 1
    For C++, better to use C++ `#include ` than C-style `#include `. q.v. https://stackoverflow.com/a/37904795/4641116 – Eljay May 21 '20 at 19:43
  • [Screenshot on this drive link](https://drive.google.com/file/d/1iTdLtf6ovfdzUDFMPOYdMMKKKvj2Q3uH/view?usp=sharing) – Sudheer Tripathi May 21 '20 at 19:46
  • @keith What different overloads? They all end up in `pow(double, double)`, no? – Barry May 21 '20 at 19:53
  • I tried this with multiple compilers, it's giving the right answer, what you got is weird, have you tried to run it again ? – magmine May 21 '20 at 20:01
  • Yes I got wrong answer for a problem on an online judge [atcoder](https://atcoder.jp) then I saw the test cases and ran it locally and discovered this. – Sudheer Tripathi May 21 '20 at 20:04
  • @Barry, really? There are overloads when the exponent is integral as is the case here for some of the code paths. – keith May 22 '20 at 11:33
  • @keith [Yes, really](https://en.cppreference.com/w/cpp/numeric/math/pow). All the integral overloads convert their arguments to `double` or `long double`. – Barry May 22 '20 at 13:32
  • @Barry, that's not what the documentation says. – keith May 22 '20 at 15:39
  • @keith Quote: _If any argument has integral type, it is cast to `double`._ – Daniel Langr May 22 '20 at 17:20
  • @Daniel Langr, ah yes, I thought that only applied to 7 but didn't see the the fact that 7 repalces 4-6 - thanks for correction. – keith May 22 '20 at 18:54
  • @keith If in doubts, it's always best to look to the Standard: _Otherwise, if any argument of arithmetic type corresponding to a `double` parameter has type `double` **or an integer type**, then all arguments of arithmetic type corresponding to `double` parameters are effectively cast to `double`._ [cmath.syn/2.2](http://eel.is/c++draft/c.math#cmath.syn-2.2). The quote from cppreference just stemmed from this sentence. – Daniel Langr May 22 '20 at 19:01

2 Answers2

11

The problem is that the output of pow is a floating point double. In your custom function you convert that output to long long, which will truncate if the value returned by pow is slightly low instead of slightly high. See Is floating point math broken?. When you call pow directly the value is kept as a double even after the multiplication, and output rounding gives you a more accurate result.

You expect the value returned by pow(100,2) to be 10000, but instead it might be 9999.99999999999 because of the way floating point works. When converted to integer, that becomes 9999; multiplied by 68, you have 679932.

On the other hand, 9999.99999999999 multiplied by 68 becomes 679999.999999999. That's close enough to 680000 that the output function << will round it for you. You can get a more exact figure if you apply output formatting.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Thanks for answering !! everything makes sense now. – Sudheer Tripathi May 21 '20 at 20:00
  • The result of `pow(100,2)` should be representable by `double` without any rounding error, so why should any rounding occur when converted to `long long`? – Daniel Langr May 21 '20 at 20:08
  • 2
    @DanielLangr you don't know the mechanics of how `pow` is calculated via `double`. Even if it's possible to represent an exact result, it could still easily be off by one LSB. – Mark Ransom May 21 '20 at 20:11
  • @MarkRansom But if `pow` itself calculated wrong result (rounded), then all the result would be wrong, wouldn't they? BTW I couldn't reproduce the problem with multiple compilers, so I'd like to know what is different in OP's case. – Daniel Langr May 21 '20 at 20:12
  • @DanielLangr `cout << double` doesn't go for the most precise representation, it uses its own rounding to hide tiny floating point errors. It's quite sophisticated. – Mark Ransom May 21 '20 at 20:16
  • 1
    @DanielLangr a few years ago I fielded a question about `pow(5,2)` coming back with 24.99999something. – user4581301 May 21 '20 at 20:30
  • @user4581301 I can understand that, but if `pow(100,2)` was calculated imprecisely, I would expect the error be observable even with `double` multiplication by `n`. But you are likely right, I see the point. I would just like to see the assembly that generates the error. Unfortunately, I can't reproduce it. – Daniel Langr May 21 '20 at 20:37
  • @DanielLangr `pow` was just doing something weird. It might not have had any short-cuts in the logic and ran the same voodoo process for 5 to the 2 that it used for difficult stuff like pi to the e. The funny thing is if the questioner had been calling `pow` instead of `std::pow` via `using namespace std;` they never would have seen the problem. I wasn't able to figure out why that made a difference. Far as I could tell it still called `pow` at the end. – user4581301 May 21 '20 at 20:46
  • @PresidentJamesK.Polk This is the best explanation and you should turn it into a standalone answer. Two important facts — I didn't realize that 679932 equals 68 times 9999. And, that casting double into integer truncates towards zero. Now, it makes perfect sense. – Daniel Langr May 22 '20 at 13:56
  • 1
    @PresidentJamesK.Polk I was considering making those changes, it's a good idea. I've just been a bit busy this morning. – Mark Ransom May 22 '20 at 14:50
0

Always write your own power function whenever needed. Change return type according to your requirement to avoid any kind of confusion.

long long int power(long long int a, long long int x) {
  static long long int ans = 1;
  if (x < 0)
    return 1 / power(a, (-1 * x));
  if (x == 1)
    return a;
  if (x == 0 or a == 1)
    return 1;
  if (x & 1)
    ans = a * power((a * a), x / 2);
  else
    ans = power((a * a), x / 2);
  return ans;
}

Here is recursive version .You can also write iterative version.

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
Nurav
  • 167
  • 1
  • 3
  • 15