0

Today, I found some strange calculations which is not known to me (Also didn't find any issues in SO regarding this) regarding pow() function of math.h in C.

The code I was experimenting was:

#include <stdio.h>
#include <math.h>

int main(){
    //1st case
    long long result = pow(2, 63);
    printf("1st case: %lld\n", result);

    //2nd case
    result = pow(2, 64);
    printf("2nd case: %lld\n", result);

    //3rd case
    printf("3rd case: %lld\n", (long long) (pow(2, 64)/12000));

    //4th case
    result = pow(2, 64);
    result = result/12000;
    printf("4th case: %lld\n", result);

    //5th case
    result = pow(2, 64) / 12000;
    printf("5th case: %lld\n", result); 

    return 0;
}

The output was:

1st case: 9223372036854775807
2nd case: 9223372036854775807
3rd case: 1537228672809129
4th case: 768614336404564
5th case: 1537228672809129

I have several questions which I am not clear about. I am using CodeBlocks 16.01 IDE and GNU GCC Compiler.

1. In first case, the output is, 2^63 = 9223372036854775807. Why is that so? The last digit is odd here. It is supposed to be 9223372036854775808.

2. In 2nd case, 2^64 = 9223372036854775807, which is the same as previously calculated 2^63 (actually this is the same for any value of exponent greater than 63). Yes, I understand that there overflow occurs for long long int data type. But why the wrong value is assigned?

3. In 3rd case and 5th case (Actually identical), the answer is correct. But in the 4th case, there is wrong answer. According to arithmetic operations, the mathematical operations in 4th case should be identical with the rest two but it is actually not. Then where is the differences?

user2736738
  • 30,591
  • 5
  • 42
  • 56
UkFLSUI
  • 5,509
  • 6
  • 32
  • 47
  • and the answer is......rounding! – Paul Ogilvie Jan 26 '18 at 08:35
  • Try to use `long double` for your variable, so it is `long double result`. Also the format of the numbers you are using might need to be `pow(2,63.0)` or `(pow(2, 64.0)/12000.0))` . This might fix your problem. – Alcaeus D Jan 26 '18 at 08:37
  • Let me know if anybody has any issue with the duplicate mark. In case of problem - I can open it. – user2736738 Jan 26 '18 at 08:38
  • Hello @coderredoc, I am the OP of this question and I also have gone through the duplicate link, I didn't find similarity there at least the 3rd question in my post is unique I guess and I also need to know the answer. Can you please review again? Thank you. – UkFLSUI Jan 26 '18 at 08:40
  • @RakibulIslam.: The reason is same. – user2736738 Jan 26 '18 at 08:44
  • https://stackoverflow.com/questions/19073450/pow-function-and-long-int-causing-problems right duplicate. – user2736738 Jan 26 '18 at 08:47
  • sorry @coderredoc, may be I couldn't make you understand, there are 3 questions in my post, my main concern is about the 3rd one. – UkFLSUI Jan 26 '18 at 08:49
  • @PaulOgilvie what about the 3rd question in my post – UkFLSUI Jan 26 '18 at 08:59
  • 1
    Your third case is still rounding: you divide the double result of `pow` by 12000 and THEN converting to integral. In your 4th case you first convert and then divide. I strongly feel most of this you could have figured out yourself by either reading about the subject, or by using a debugger to step through the code. – Paul Ogilvie Jan 26 '18 at 10:19

1 Answers1

5

Aren't these warnings (compiled like gcc prog.c -Wall -Wextra) enough for you?

prog.c: In function 'main':
prog.c:6:24: warning: overflow in conversion from 'double' to 'long long int' changes value from '(double)9.2233720368547758e+18' to '9223372036854775807' [-Woverflow]
     long long result = pow(2, 63);
                        ^~~
prog.c:10:14: warning: overflow in conversion from 'double' to 'long long int' changes value from '(double)1.8446744073709552e+19' to '9223372036854775807' [-Woverflow]
     result = pow(2, 64);
              ^~~
prog.c:17:14: warning: overflow in conversion from 'double' to 'long long int' changes value from '(double)1.8446744073709552e+19' to '9223372036854775807' [-Woverflow]
     result = pow(2, 64);
              ^~~

I mean the overflow occurs, and then rounding happens, which gives you this output, since the actual value simply does not fit in that data type. If it was a long double, things would be different. (Do not forget to print with %Lf.)

Read more in What causes floating point rounding errors?


PS: In case 3 you do the division before converting to long long, so it uses the floating point value, which is correct, as Barmar correctly commented.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 2
    Maybe OP is ignoring those warnings - +1 for the effort. – user2736738 Jan 26 '18 at 08:40
  • Actually, I asked three questions there and my main concern is about the 3rd one. How the calculation steps are done that when divided with 12000 in the same statement gets me correct result while separating them makes the differences. And also, when divided with 12000, the overflow does not occurs for value greater than 64. Is it something like the value of pow() is calculated to some higher extinct inside and after dividing with 12000, I get the answer. – UkFLSUI Jan 26 '18 at 08:46
  • 2
    @RakibulIslam In case 3 you do the division before converting to `long long`, so it uses the floating point value, which is correct. – Barmar Jan 26 '18 at 09:34
  • Thanks, now I understood. – UkFLSUI Jan 26 '18 at 09:35