2

Here is my code in C++. I'm using g++-11 with gcc version 11.2.0.

#include<iostream> 
#include<vector> 
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;


int main()
{
    string t="90071992547409929007199254740993";
    //separateNumbers(t);
    unsigned long long stringNum=0;
    for (int j=16;j<32;j++)
    {
        stringNum += (t[j]-'0')*pow(10,32-j-1);
        
    }
    cout << stringNum;
    
    return 0;
}

This simple code, which I expected to get the last 16 digits as a number, gives 9929007199254740992, not 9929007199254740993.

However, the change of code

#include<iostream> 
#include<vector> 
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;


int main()
{
    string t="90071992547409929007199254740993";
    //separateNumbers(t);
    unsigned long long stringNum=0;
    for (int j=16;j<32;j++)
    {
        unsigned long long temp = (t[j]-'0')*pow(10,32-j-1);
        stringNum += temp;
        
    }
    cout << stringNum;
    
    return 0;
}

gives the desired result, 9007199254740993. How can we explain such a difference between two codes?

user124697
  • 325
  • 1
  • 3
  • 7
  • `pow` is a floating point function -- it is not an integer-based function. Do not use floating-point functions if your program is supposed to be dealing with integers. – PaulMcKenzie Apr 15 '22 at 02:15
  • @PaulMcKenzie Aha, I see! Thank you very much! Maybe that is the problematic part. – user124697 Apr 15 '22 at 04:18

1 Answers1

1

As mentioned in @PaulMcKenzie's comment, the source of the difference is mixing floating-point arithmetics with integers.

Some more info for those who are interested:

The difference manifests in the last iteration of your loop.
In the first case, you have:

stringNum += (t[j]-'0')*pow(10,32-j-1);

which is equivalent to:

stringNum = stringNum + (t[j]-'0')*pow(10,32-j-1);

pow returns a double, and so the expresion (t[j]-'0')*pow(10,32-j-1) will be of type double, as well as: stringNum + (t[j]-'0')*pow(10,32-j-1). The value you expect (9007199254740993) cannot be represented in a double. The closest value is: 9007199254740992. When assigned back to integer stringNum it stays 9007199254740992.
You can see more about this issue here: Is floating point math broken?.

You can also see this easily if you try the following lines (your compiler will also give a warning about the possible loss of precision):

double d = 9007199254740993;
std::cout.precision(17);
std::cout << d << std::endl;

Which prints: 9007199254740992.

In the second case you assign the value for the last digit to temp:

unsigned long long temp = (t[j]-'0')*pow(10,32-j-1);

(t[j]-'0')*pow(10,32-j-1) is a double with value 3, which incidently can be represented in a double. Then it gets assigned into an integer temp which is also 3. When you execute:

stringNum += temp;

You use integer arithmetic and get the correct result: 9007199254740993.

wohlstad
  • 12,661
  • 10
  • 26
  • 39