我注意到在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是l值,在这种情况下,因为编译器知道输出将很长很长,所以看起来不需要在加法期间先将左侧转换为double,然后再转换为long long。同样,有人犯错误并省略显式强制转换也很容易,因为精确度错误仅对某些数字才可见。
这是C ++标准的双重转换部分还是VS的实现?
答案 0 :(得分:2)
根据标准[expr.ass/7]:
形式为E1 op = E2的表达式的行为等同于 E1 = E1 op E2,除了E1仅被评估一次。
因此,即使最终结果可能需要再次转换回a
的类型(请参见[expr.ass/3]),也适用常规的算术转换。
在您的示例中,[expr.arith.conv/1.3]对a += b
的{{1}}转换为a
。使用浮点算术执行加法。
对于您的特定值,double
的精确整数值和加法结果的精确整数值无法用a
精确表示,因此结果不精确。
对于double
,两个操作数均为a += (long long)b
,因此不需要转换。加法是使用整数算术执行的。
在您的特定示例中,long long
的值恰好在b
的精确表示范围内。因此,从整数文字转换为double
到用double
返回到long long
的转换恰好会返回相同的值。因此加法结果是准确的。