(IEEE 754)
double
不是Cpp17LessThanComparable,因为由于NaN
,<
不是严格的弱序关系。因此,违反了std::min<double>
和std::max<double>
的Requires元素。所有引用都遵循n4800。24.7.8中给出了
std::min
和std::max
的规范:template constexpr const T& min(const T& a, const T& b); template constexpr const T& max(const T& a, const T& b); 要求:[...]类型T应该是Cpp17LessThanComparable(表24)。
表24定义了Cpp17LessThanComparable并表示:
要求:
<
是一个严格的弱序关系(24.7)第24.7/4节定义了严格弱序。特别地,对于
<
,它指出:“如果我们将equiv(a,b)
定义为!(a < b) && !(b < a)
,则equiv(a,b) && equiv(b,c)
意味着equiv(a,c)
”。根据IEEE 754,现在有equiv(0.0,NaN) == true
,equiv(NaN,1.0) == true
,但是equiv(0.0,1.0) == false
,因此我们得出结论:<
不是严格弱序。因此(IEEE 754)的double
不是Cpp17LessThanComparable,这违反了std::min
和std::max
的Requires条款。最后,15.5.4.11/1说:
任何函数的前提条件都会导致未定义行为[...]。更新1: 问题的重点并不在于争论
std::min(0.0,1.0)
是否未定义,以及当程序评估此表达式时可能发生什么。 它返回0.0
。 期。 (我从未怀疑过。)重点是展示标准的一个(可能的)缺陷。在追求精度的可赞任务中,标准经常使用数学术语,弱严格排序只是其中的一个例子。在这些情况下,必须使用数学精度和推理。
例如,看看维基百科关于strict weak ordering的定义。它包含四个要点,每一个要点都以“对于S中的每个x[...]”开始。没有一个说“对于使算法有意义的S中的某些值x”(什么算法?)。此外,std::min的规范清楚地说明“T应为Cpp17LessThanComparable”,这意味着<小于号>是T上的一种弱严格排序。因此,T扮演了维基百科页面中的S集合的角色,当考虑T的值的整体时,四个要点必须成立。
显然,NaN与其他双精度值非常不同,但它们仍然是可能的值。我没有在标准中发现任何(相当大的1719页的标准,因此出现了这个问题和language-lawyer标签)数学上导致结论:只要不涉及NaNs,std::min可以使用doubles。
实际上,可以说NaN是好的,而其他双精度浮点数才是问题所在!事实上,要记住有几个可能的NaN双精度值(其中2^52-1个,每个都携带不同的有效负载)。考虑包含所有这些值和一个“正常”双精度浮点数(比如42.0)的集合S。用符号表示为S={42.0, NaN_1,...,NaN_n}。结果表明,在S上<是严格弱排序(证明留给读者)。C++委员会在指定std::min时是否考虑了这组值呢?他们的意思是“请勿使用任何其他值,否则将破坏严格弱排序,std::min的行为将是未定义的”?我敢打赌他们没有,但我更愿意在标准中阅读这一点,而不是推测“某些值”的含义。
更新2: 将std::min的声明(上文)与clamp 24.7.9的声明进行对比:
template constexpr const T& clamp(const T& v, const T& lo, const T& hi); Requires: The value of lo shall be no greater than hi. For the first form, type T shall be Cpp17LessThanComparable (Table 24). [Note : If NaN is avoided, T can be a floating-point type. — end note]
在这里,我们清楚地看到了一些内容,它说“
std::clamp
可以处理双精度浮点数,只要不涉及 NaN。” 我正在寻找与 std::min
相同类型的句子。值得注意的是,巴里在他的post中提到的段落 [structure.requirements]/8。显然,这是在 C++17 之后添加的(来自P0898R0):
“本文档中定义的任何概念所需的操作不必是总函数;也就是说,对于某些所需操作的参数,可能导致所需语义无法满足。[例如:严格全序概念(17.5.4)的所需
<
运算符在操作 NaN 时不满足该概念的语义要求。——结束示例]这不影响类型是否满足该概念。”这是明确尝试解决我在这里提出的问题,但是针对概念的上下文(正如巴里指出的,Cpp17LessThanComparable 不是一个概念)。此外,在我看来,这段话也缺乏精确性。