【问题标题】:C++ precision errors when adding double to long long [duplicate]将double添加到long long时的C ++精度错误[重复]
【发布时间】:2018-12-09 18:05:17
【问题描述】:

我注意到在 Visual Studio 中将 double 添加到 long long 时会出现精度错误。例如:

long long a = 44981600439878676;
double b = 234567890;
a += b;

a 的结果是 44981600674446560 但应该是 44981600674446566。x32 和 x64 都会发生这种情况。

但是以下返回正确的值:

long long a = 44981600439878676;
double b = 234567890;
a += (long long)b;

我注意到在反汇编中,在没有显式转换的第一种情况下,有

0116A892  call        __ltod3 (011619DDh)  
0116A897  addsd       xmm0,mmword ptr [b]  
0116A89C  call        __dtol3 (01161A05h) 

而在第二种情况下,__ltod3 未被调用。我正在用 VC++ 编译器解释这一点,默认情况下首先将 long long 转换为 double,然后再将 double 转换为 long long,因为 double 类型比 long long 更简单。这样,由于 __ltod3 和 int64 包含的值太大,我们会丢失精度。但另一方面,a 是左值,在这种情况下,因为编译器知道输出会是 long long,所以在加法过程中,看起来没有必要先将左侧转换为 double,然后再转换为 long long。此外,很容易有人犯错误并忽略显式转换,因为精度错误只会对某些数字变得可见。

这种双重转换是 C++ 标准的一部分还是 VS 的实现?

【问题讨论】:

  • double 的精度低于 long long。
  • 您应该能够自己解决这个问题,在您尝试围绕以下两个事实进行思考之后:1. sizeof(double)sizeof(long long) 相同。 2) double 似乎能够表示比 long long 更大的值,例如 1e100,或 1 后跟一百个零。现在,问问自己这怎么可能,如果 long longdouble 占用相同的字节数,那么您应该能够找出问题的答案。
  • 我只是想知道在添加之后最终结果是否会存储在 long long 中,为什么左侧默认转换为 double 然后又转换为 long long?如果我希望默认情况下不丢失精度怎么办。在复杂的代码中,可能有太多地方需要检查是否缺少显式转换。
  • 这就是 c++ 算术的工作原理,您必须始终牢记这一点。这也是为什么隐式转换是邪恶的。

标签: c++ precision long-integer assign


【解决方案1】:

按照标准[expr.ass/7]

形式为 E1 op= E2 的表达式的行为等价于 E1 = E1 op E2 除了 E1 只计算一次。

因此,即使最终结果可能再次需要转换回a 的类型(参见[expr.ass/3]),也适用通常的算术转换。

对于您的示例中的a += b[expr.arith.conv/1.3] a 被转换为double。使用浮点运算执行加法。 使用您的特定值a 的确切整数值和加法结果的确切整数值不能完全用double 表示,因此结果不准确。

对于a += (long long)b,两个操作数都是long long,因此不需要转换。加法是使用整数算术执行的。

在您的特定示例中,b 的值恰好在double 的精确表示范围内。因此,从整数文字到 double 并返回到 long long(long long)b 的转换恰好返回相同的值。所以加法的结果是准确的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-03
    • 2013-12-31
    • 1970-01-01
    • 2014-09-30
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多