在 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;
}
我会参考下面的代码来解释我的问题。
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;
}