在 C++ 中使用 long long int 与 double 时表达式求值的差异

Difference in evaluation of expression when using long long int vs double in c++

我会参考下面的代码来解释我的问题。

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
}

我无法理解为什么语句 b 和 c 的计算结果为 64,而 d 的计算结果为 0,而这恰好是正确答案。

e 和 f 的计算结果都为 0,所以差异是由于从 lli_a 中减去我假设的。我认为没有任何溢出问题,因为每个术语的个别值都正确。

double是浮点型。浮点类型的精度有限。它们不能代表所有数字——甚至不能代表所有有理数。简单地说(在您的系统上)603828039791327040 是一个无法用 double 数据类型表示的数字。可表示的最接近值恰好与精确值相差 64。

您可以(很可能)通过使用 long double 获得预期结果,它(通常)可以表示 long long 的所有值 - 或者您可以首先避免使用浮点数。

一些代码可以引导您完成它,底线是不要隐式地将双精度数与整数混合

#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;
}