将 double 添加到 long long 时出现 C++ 精度错误

C++ precision errors when adding double to long long

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

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

a 的结果是 44981600674446560,但应该是 44981600674446566。它发生在 x32 和 x64 上。

然而下面的returns正确值:

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 的实现?

根据标准[expr.ass/7]:

The behavior of an expression of the form E1 op= E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.

因此,即使最终结果可能再次需要转换回 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 的转换恰好返回相同的值。因此加法结果是准确的。