1

I'll refer to the below code to explain my question.

typedef long long int ll;

void func(){
    ll lli_a = 603828039791327040;
    ll lli_b = 121645100408832000;
    double d_b = (double)lli_b;
    cout << "a " << lli_b - d_b << endl; \\0
    cout << "b " << (lli_a - 4*lli_b) - (lli_a - 4*d_b) << endl; \\64
    cout << "c " << (lli_a - 4*lli_b) - (lli_a - (ll)4*d_b) << endl; \\64
    cout << "d " << (lli_a - 4*lli_b) - (lli_a - 4*(ll)d_b) << endl; \\0
    cout << "e " << 4*(ll)d_b - 4*d_b << endl; \\0
    cout << "f " << 4*(ll)d_b - (ll)4*d_b << endl; \\0
}

I'm unable to understand why statements b and c have evaluated to 64, while d has evaluated to 0, which happens to be the correct answer.

Both e and f evaluate to 0, so the difference is coming because of subtraction from lli_a I assume. I don't think there is any overflow issue as individual values for each term are coming correctly.

Haha hehe
  • 13
  • 2
  • Two notes about the code: First of all don't create aliases like `ll`, that actually makes the code *harder* to read, understand and maintain; Secondly whenever you feel the need to do a C-style cast (like `(ll)d_b`) in C++ you should take that as a sign that you're doing something wrong. – Some programmer dude Jan 13 '22 at 06:45
  • An IEEE-754 `double` has ~16 decimal digits of precision. Your constants have 19 digits. – Adrian Mole Jan 13 '22 at 06:47

2 Answers2

1

double is a floating point type. Floating point types have limited precision. They cannot represent all numbers - not even all rational numbers. Simply (on your system) 603828039791327040 is a number that cannot be represented by the double datatype. The closest value that is representable happens to be 64 away from the precise value.

You can (likely) get the expected result by using long double which (typically) can represent all values of long long - or you could avoid using floating point in the first place.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

Some code to walk you through it, bottom line don't mix doubles with ints implicitly

#include <cassert>
#include <iostream>
#include <type_traits>

// typedef long long int ll; NO , use using and never use aliasing to safe a bit of typing. Aliases are there to introduce meaning not shortcuts

//using namespace std; // also NO

int main() 
{
    long long int lli_a = 603828039791327040;
    long long int lli_b = 121645100408832000;
    //double d_b = (double)lli_b;                     // No this is C++ don't use 'C' style casts
    double d_b = static_cast<double>(lli_b);

    assert(static_cast<long long int>(d_b) == lli_b); // you are in luck the double can represent your value exectly, NOT guaranteed

    std::cout << "a " << lli_b - d_b << "\n"; //  endl; \\0 don't use endl unless you have a good reason to flush

    long long int lli_b4 = 4 * lli_b;
    
    // use auto to show you this expression evaluates to a double!
    auto lli_d_b4 = (lli_a - static_cast<long long int>(4) * d_b); // d_b is double!!! what do you want to do here? Use it as a long long int then cast it first
    static_assert(std::is_same_v<double, decltype(lli_d_b4)>);

    auto result_c = lli_b4 - lli_d_b4;
    // result c is still a double!
    static_assert(std::is_same_v<double, decltype(result_c)>);
    std::cout << "c " << result_c << "\n";
    
    // long story short don't mix types implicitly and use "C++" style cast explicitly to get the results you want

    /*

    
        cout << "b " << (lli_a - 4 * lli_b) - (lli_a - 4 * d_b) << endl; \\64
        cout << "c " << (lli_a - 4 * lli_b) - (lli_a - (ll)4 * d_b) << endl; \\64
        cout << "d " << (lli_a - 4 * lli_b) - (lli_a - 4 * (ll)d_b) << endl; \\0
        cout << "e " << 4 * (ll)d_b - 4 * d_b << endl; \\0
        cout << "f " << 4 * (ll)d_b - (ll)4 * d_b << endl; \\0
        */

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19