M.M*_*M.M 8 c++ floating-point language-lawyer c++14
在以下代码中:
#include <cstdint>
#include <cinttypes>
#include <cstdio>
using namespace std;
int main() {
double xd = 1.18;
int64_t xi = 1000000000;
int64_t res1 = (double)(xi * xd);
double d = xi * xd;
int64_t res2 = d;
printf("%" PRId64"\n", res1);
printf("%" PRId64"\n", res2);
}
Run Code Online (Sandbox Code Playgroud)
使用g++ -std=c++14针对32位Windows的v4.9.3 获取输出:
1179999999
1180000000
Run Code Online (Sandbox Code Playgroud)
这些值是否允许不同?
我期望,即使编译器使用比double计算更高的内部精度xi * xd,它也应该一致地执行此操作.浮动转换中的精确度丢失是实现定义的,并且该计算的精度也是实现定义的 - [c.limits]/3表示FLT_EVAL_METHOD应该从C99导入.IOW我希望不允许xi * xd在一行上使用不同的精度而不是在另一行上使用不同的精度.
注意:这是故意的C++问题,而不是C问题 - 我相信这两种语言在这个领域有不同的规则.
即使编译器使用比 double 更高的内部精度来计算 xi * xd,它也应该始终如一地执行此操作
无论是否需要(下面讨论),这显然不会发生:Stackoverflow 上充斥着来自人们的问题,他们看到同一程序中看似相似的计算无缘无故地发生了变化。
C++ 标准草案 n3690 说(强调我的):
浮点操作数的值和浮点表达式的结果可以用比类型所要求的更高的精度和范围来表示;类型不会因此改变。62
62) 强制转换和赋值运算符仍必须执行 5.4、5.2.9 和 5.17 中描述的特定转换。
因此 - 与 MM 的评论一致,与我之前的编辑相反 - 这是必须四舍五入为 64 位的强制转换版本(double)-在问题中记录的运行中显然恰好是 >= 1180000000 - 在截断为整数之前。更一般的情况 sans 62) 使编译器可以自由地在其他情况下不提前舍入。double
[c.limits]/3 表示 FLT_EVAL_METHOD 应该从 C99 导入。IOW 我预计不应允许在一行上使用与另一行不同的 xi * xd 精度。
无论 FLT_EVAL_METHOD 的值如何,任何浮点表达式都可以收缩,也就是说,计算时就好像所有中间结果都具有无限范围和精度(除非 #pragma STDC FP_CONTRACT 关闭)
正如 tmyklebu 评论的那样:
转换和赋值消除了任何无关的范围和精度:这模拟了将扩展精度 FPU 寄存器中的值存储到标准大小的内存位置的操作。
这最后与标准的“62)”部分一致。
MM评论:
STDC FP_CONTRACT 似乎没有出现在 C++ 标准中,而且我也不清楚 C99 行为“导入”到什么程度
没有出现在我看过的草稿中。这表明 C++ 不保证其可用性,保留上面提到的默认值“任何浮点表达式都可以收缩”,但我们知道根据 MM 注释以及强制转换上方的 Standard 和 cppreference 引号(double)是一个例外,强制舍入为 64 位。
上面提到的 C++ 标准草案说道<cfloat>:
内容与标准C 库头文件相同。另请参见:ISO C 7.1.5、5.2.4.2.2、5.2.4.2.1。
如果需要这些 C 标准之一,STDC FP_CONTRACT则它更有可能被 C++ 程序移植使用,但我还没有调查支持的实现。