在运行32位GCC 7.3.0的特定在线评判器中,这。
#include <iostream>
volatile float three = 3.0f, seven = 7.0f;
int main()
{
float x = three / seven;
std::cout << x << '\n';
float y = three / seven;
std::cout << (x == y) << '\n';
}
输出
0.428571
0
在我看来,这似乎违反了IEEE 754标准,因为该标准要求基本运算必须正确取整。我知道IEEE 754浮点运算的非确定性有几个原因,如下所述 此处但我看不出其中有什么适用于这个例子。以下是我考虑的一些问题。
float
,这将迫使这两个值都四舍五入为 float
精度。three
和 seven
是 volatile
所以这两个计算必须在运行时完成。这是否一定说明在线评判系统不符合IEEE 754的要求?
另外,去掉语句打印 x
,添加一个语句来打印 y
或制作 y
volatile都会改变结果。这似乎与我对C++标准的理解相矛盾,我认为C++标准要求赋值要舍弃多余的精度。
感谢geza指出这是一 已知问题. 不过我还是希望得到一个明确的答案,这是否符合C++标准和IEEE 754,因为C++标准似乎要求赋值来舍弃多余的精度。下面是N4860草案[expr.pre]中的一段话。
浮点操作数的值和浮点表达式的结果可以用比类型要求更高的精度和范围来表示,但类型不会因此而改变。50
50)投值和赋值运算符仍然必须按照7.6.1.3、7.6.3、7.6.1.8和7.6.19中的描述执行其特定的转换。
这是否一定说明在线评判系统不符合IEEE 754?
是的,但有一些小的警告。
其一,C++不能仅仅 "符合 "IEEE 754。必须对C++中的事物如何与IEEE 754绑定(连接)有一定的规范,比如语句中的 float
格式为IEEE-754二进制32,即 x / y
使用IEEE-754除法,以此类推。C++ 2017草案N4659提到了LIA-1,但我没有看到它明确要求使用LIA-1,即使是 std::numeric_limits<float>::is_iec559
报告属实,LIA-1 显然只是建议语言绑定.
C++标准告诉我们这样一个事实 std::numeric_limits<float>::is_iec559
报告属实即表示 float
类型符合ISOIECIEEE 60559,实际上就是IEEE 754-2008。但是,除了绑定问题外,我在C++标准中并没有看到有一条声明使8[expr]13无效("浮动操作数的值和浮动表达式的结果可以用比类型所要求的更大的精度和范围来表示;类型不会因此而改变。"),当 is_iec559
是真的。虽然投射和转换运算符确实必须 "执行其特定的转换"(脚注64),而且这迫使 float y = three / seven;
以产生正确的IEEE-754二进制32结果,即使使用二进制64或英特尔的80位浮点数进行除法,如果只使用了一点多余的精度,也可能不会迫使它产生正确的结果。(如果使用了至少48位的精度,当四舍五入到二进制32格式的24位时,除法就不会出现双进位错误。如果使用的多余位数较少,可能有些情况下会出现双倍进位错误)。)
我相信 is_iec559
是为了表示合理的约束,而题中所示的行为确实违反了这一点。特别是,题中所示的缺陷是由于没有将除法中使用的多余精度四舍五入到实际的 float
型;它不是由上面提到的假设性使用不够的多余精度造成的。