gpt4 book ai didi

c++ - std::min(0.0, 1.0) 和 std::max(0.0, 1.0) 会产生未定义的行为吗?

转载 作者:行者123 更新时间:2023-12-02 02:35:11 31 4
gpt4 key购买 nike

这个问题很清楚。下面给出了我认为这些表达式可能产生未定义行为的原因。我想知道我的推理是对还是错,为什么。

短读 :

(IEEE 754) double不是 Cpp17LessThanComparable,因为 <由于 NaN 不是严格的弱排序关系.因此,需要 std::min<double> 的元素和 std::max<double>被侵犯。

长读 :

所有引用文献都遵循 n4800 . std::min规范和 std::max在 24.7.8 中给出:

template<class T> constexpr const T& min(const T& a, const T& b);
template<class T> constexpr const T& max(const T& a, const T& b);
Requires: [...] type T shall be Cpp17LessThanComparable (Table 24).



表 24 定义了 Cpp17LessThanComparable 并表示:

Requirement: < is a strict weak ordering relation (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 的 Requires 条款和 std::max .

最后,15.5.4.11/1 说:

Violation of any preconditions specified in a function’s Requires: element results in undefined behavior [...].



更新 1:

问题的重点不是要争辩 std::min(0.0, 1.0)未定义,当程序评估此表达式时,任何事情都可能发生。它返回 0.0 .时期。 (我从来没有怀疑过。)

关键是要显示标准的(可能的)缺陷。在对精度的一项值得称赞的追求中,标准经常使用数学术语,弱严格排序只是一个例子。在这些场合,数学的精确性和推理性必须一路走好。

例如,看看维基百科对 strict weak ordering 的定义.它包含四个要点,每个要点都以“For every x [...] in S...”开头。他们都没有说“对于 S 中对算法有意义的某些值 x”(什么算法?)。另外, std::min的规范明确地说“ T 应为Cpp17LessThanComparable”,这意味着 <T 上的严格弱排序.因此, T在维基百科页面中扮演集合 S 的角色,当值 T 时,四个要点必须成立。被整体考虑。

显然,NaN 与其他 double 值完全不同,但它们仍然是可能的值。我在标准(相当大,1719 页,因此这个问题和语言律师标签)中没有看到任何 数学 得出的结论是 std::min只要不涉及 NaN, double 就可以了。

实际上,人们可以争辩说 NaN 很好,而其他 double 是问题所在!事实上,回想一下,有几个可能的 NaN double 值(其中 2^52 - 1 个,每个都携带不同的有效载荷)。考虑包含所有这些值和一个“正常” double 值的集合 S,例如 42.0。在符号中,S = { 42.0, NaN_1, ..., NaN_n }。原来 <是 S 上的严格弱排序(证明留给读者)。这组值是 C++ 委员会在指定 std::min 时考虑的吗?如“请不要使用任何其他值,否则严格的弱排序将被破坏, std::min 的行为未定义”?我敢打赌不是,但我更愿意在标准中阅读它而不是推测“某些值”的含义。

更新 2:

对比 std::min的声明(上)与 clamp 24.7.9:

template<class T>
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 可以用于 double ,前提是不涉及 NaN”。我正在为 std::min 寻找相同类型的句子.

值得注意的是 Barry 在他的 post 中提到的 [structure.requirements]/8 段。 .显然,这是在 C++17 之后添加的,来自 P0898R0 ):

Required operations of any concept defined in this document need not be total functions; that is, some arguments to a required operation may result in the required semantics failing to be satisfied. [Example: The required < operator of the StrictTotallyOrdered concept (17.5.4) does not meet the semantic requirements of that concept when operating on NaNs. — end example ] This does not affect whether a type satisfies the concept.



这是解决我在这里提出的问题的明确尝试,但在概念的背景下(正如巴里所指出的,Cpp17LessThanComparable 不是一个概念)。此外,恕我直言,这一段也缺乏精确性。

最佳答案

免责声明:我不知道完整的 C++ 标准,我对关于浮点数的说法做了一些研究。我确实了解 IEEE 754-2008 浮点数和 C++。

是的,你是对的,这是 C++17 标准的未定义行为。

简读:

标准没有说std::min(0.0, 1.0);是未定义的行为,它说 constexpr const double& min(const double& a, const double& b);是未定义的行为。这意味着,它不是应用未定义的函数,而是未定义的函数声明本身。就像数学上的情况一样:正如您所指出的,在 IEEE 754 浮点数的整个范围内不可能使用最小函数。

但是未定义的行为并不一定意味着崩溃或编译错误。这只是意味着它不是由 C++ 标准定义的,并特别说明它可能“在翻译或程序执行期间以环境特征的文档化方式运行”

为什么不应该使用 std::min double :

因为我意识到以下长篇阅读部分可能会很无聊,所以这里有一个比较中 NaN 风险的玩具示例(我什至不尝试排序算法......):

#include <iostream>
#include <cmath>
#include <algorithm>

int main(int, char**)
{
double one = 1.0, zero = 0.0, nan = std::nan("");

std::cout << "std::min(1.0, NaN) : " << std::min(one, nan) << std::endl;
std::cout << "std::min(NaN, 1.0) : " << std::min(nan, one) << std::endl;

std::cout << "std::min_element(1.0, 0.0, NaN) : " << std::min({one, zero, nan}) << std::endl;
std::cout << "std::min_element(NaN, 1.0, 0.0) : " << std::min({nan, one, zero}) << std::endl;

std::cout << "std::min(0.0, -0.0) : " << std::min(zero, -zero) << std::endl;
std::cout << "std::min(-0.0, 0.0) : " << std::min(-zero, zero) << std::endl;
}

在我的 macbookpro 上使用 Apple LLVM 版本 10.0.0 (clang-1000.10.44.4) 进行编译时(我做到了精度,因为这是未定义的行为,所以理论上这可能在其他编译器上有不同的结果)我得到:
$ g++ --std=c++17 ./test.cpp
$ ./a.out
std::min(1.0, NaN) : 1
std::min(NaN, 1.0) : nan
std::min_element(1.0, 0.0, NaN) : 0
std::min_element(NaN, 1.0, 0.0) : nan
std::min(0.0, -0.0) : 0
std::min(-0.0, 0.0) : -0

这意味着与您的假设相反, std::min不对称 当涉及 NaN 时,甚至 -0.0 .并且 NaN 不会传播。短篇故事:这确实让我对之前的项目感到有些痛苦,在那里我不得不实现自己的 min函数按照项目规范的要求在两侧正确传播 NaN。因为 std::min double 未定义 !

IEEE 754:

正如您所指出的,IEEE 754 浮点数(或 ISO/IEC/IEEE 60559:2011-06,这是 C11 标准使用的规范,见下文,它或多或少地复制了 C 语言的 IEEE754)没有严格弱排序,因为 NaN 违反了不可比性的传递性 ( fourth point of the Wikipedia page )

有趣的是,IEE754 规范已于 2008 年修订(现更名为 IEEE-754-2008), which includes a total ordering function .事实上,C++17 和 C11 都没有实现 IEE754-2008,而是实现了 ISO/IEC/IEEE 60559:2011-06

但谁知道呢?也许这会在 future 改变。

长读:

首先,让我们先回顾一下未定义行为实际上是什么,来自 the same standard draft you linked (重点是我的):

undefined behavior behavior for which this document imposes no requirements

[Note 1 to entry: Undefined behavior may be expected when this document omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. Evaluation of a constant expression never exhibits behavior explicitly specified as undefined in Clause 4 through Clause 14 of this document (7.7). —end note]



没有“产生”未定义行为这样的东西。它只是在 C++ 标准中没有定义的东西。这可能意味着您可以自行承担使用它并获得正确结果的风险(例如通过执行 std::min(0.0, 1.0); 或者它可能会引发警告甚至编译错误,如果您发现一个对浮点数非常小心的编译器!

关于子集……你说:

I do not see anything in the Standard (which is quite big, 1719 pages, and hence this question and the language-lawyer tag) that mathematically leads to the conclusion that std::min is fine with doubles provided that NaNs are not involved.



我自己也没有阅读标准,但是从您发布的部分来看,标准似乎已经说这很好。我的意思是,如果你构造 新型T 包装 double ,不包括 NaN,然后​​是 template<class T> constexpr const T& min(const T& a, const T& b); 的定义 应用于您的新型 将具有定义的行为,并且行为与您对最小函数的期望完全相同。

我们也可以看看操作的标准定义 <double ,它在第 25.8 节浮点类型的数学函数中定义,它说这不是很有帮助:

The classification / comparison functions behave the same as the C macros with the corresponding names defined in the C standard library. Each function is overloaded for the three floating-point types. See also: ISO C 7.12.3, 7.12.4



什么 the C11 standard说? (因为我猜 C++17 不使用 C18)

The relational and equality operators support the usual mathematical relationships between numeric values. For any ordered pair of numeric values exactly one of the relationships — less, greater, and equal — is true. Relational operators may raise the ‘‘invalid’’ floating-point exception when argument values are NaNs. For a NaN and a numeric value, or for two NaNs, just the unordered relationship is true.241)



至于 C11 使用的规范,则在该规范的附件 F 下:

This annex specifies C language support for the IEC 60559 floating-point standard. The IEC 60559 floating-point standard is specifically Binary floating-point arithmetic for microprocessor systems, second edition (IEC 60559:1989), previously designated IEC 559:1989 and as IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE 754−1985). IEEE Standard for Radix-Independent Floating-Point Arithmetic (ANSI/IEEE854−1987) generalizes the binary standard to remove dependencies on radix and word length. IEC 60559 generally refers to the floating-point standard, as in IEC 60559 operation, IEC 60559 format, etc.

关于c++ - std::min(0.0, 1.0) 和 std::max(0.0, 1.0) 会产生未定义的行为吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55153210/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com