std::min/float monoid 的身份元素
Identity element for std::min/float monoid
在this(很有意思)talk speaker 提出了一个问题:
What is the e value for the float/std::min monoid.
换句话说:由标准 C++ 浮点数和 std::min 操作组成的幺半群的标识元素是什么?演讲者说这个答案很“有趣”。
我认为std::numeric_limits<float>::infinity()
应该是答案,代码为证:
const auto max = numeric_limits<float>::max();
const auto min = numeric_limits<float>::min();
const auto nan = numeric_limits<float>::signaling_NaN();
const auto nan2 = numeric_limits<float>::quiet_NaN();
const auto inf = numeric_limits<float>::infinity();
const auto minus_inf = -inf;
cout << std::min(max, inf) << "\n";
cout << std::min(min, inf) << "\n";
cout << std::min(nan, inf) << "\n";
cout << std::min(nan2, inf) << "\n";
cout << std::min(inf, inf) << "\n";
cout << std::min(minus_inf, inf) << "\n";
打印:
3.40282e+38
1.17549e-38
nan
nan
inf
-inf
我们在测试中调用 std::min 时总是得到左边的参数。无穷大是正确答案还是我遗漏了什么?
编辑:我似乎漏掉了什么。这个:
cout << std::min(nan, inf) << "\n";
cout << std::min(inf, nan) << "\n";
打印:
nan
inf
所以在 NaN 恶作剧的情况下,我们会根据参数的顺序得到不同的答案。
affinely-extended 实数(即包括 +/-inf 但不包括 NaN)上的 min
显然是一个幺半群。
然而,将任何东西与 NaN
进行比较的结果是 而不是 false,而是“unordered”。这意味着 <
只是 float
上的偏序,因此 std::min<float>
(根据 <
定义)不是幺半群。
在 IEEE 754 中有一个 totalOrder
谓词 - 虽然我不知道它是如何在 C++ 中公开的,如果有的话。我们可以用这个代替 <
来写我们自己的 min
,并且 会 形成一个幺半群……但它不会是 std::min
.
为了确认,我们可以在 godbolt 上编译您的代码的一个变体,看看它在实践中是如何实现的:
与comiss
进行比较,有可能的结果
UNORDERED: ZF,PF,CF←111;
GREATER_THAN: ZF,PF,CF←000;
LESS_THAN: ZF,PF,CF←001;
EQUAL: ZF,PF,CF←100;
并指定
The unordered result is returned if either source operand is a NaN (QNaN or SNaN).
分支已通过 jbe
完成,这将
Jump short if below or equal (CF=1 or ZF=1).
你可以看到 UNORDERED 结果实际上被这个条件分支视为小于 和 相等。
所以,假设这是 IEEE 754 提到的无序比较的合法模型,应该允许两者都存在
min(min(+inf, NaN), -inf) =
min(+inf, -inf) = -inf
和
min(+inf, min(NaN, -inf)) =
min(+inf, NaN) = +inf
这意味着 min<float>
不是关联的,因此不是幺半群。
在this(很有意思)talk speaker 提出了一个问题:
What is the e value for the float/std::min monoid.
换句话说:由标准 C++ 浮点数和 std::min 操作组成的幺半群的标识元素是什么?演讲者说这个答案很“有趣”。
我认为std::numeric_limits<float>::infinity()
应该是答案,代码为证:
const auto max = numeric_limits<float>::max();
const auto min = numeric_limits<float>::min();
const auto nan = numeric_limits<float>::signaling_NaN();
const auto nan2 = numeric_limits<float>::quiet_NaN();
const auto inf = numeric_limits<float>::infinity();
const auto minus_inf = -inf;
cout << std::min(max, inf) << "\n";
cout << std::min(min, inf) << "\n";
cout << std::min(nan, inf) << "\n";
cout << std::min(nan2, inf) << "\n";
cout << std::min(inf, inf) << "\n";
cout << std::min(minus_inf, inf) << "\n";
打印:
3.40282e+38
1.17549e-38
nan
nan
inf
-inf
我们在测试中调用 std::min 时总是得到左边的参数。无穷大是正确答案还是我遗漏了什么?
编辑:我似乎漏掉了什么。这个:
cout << std::min(nan, inf) << "\n";
cout << std::min(inf, nan) << "\n";
打印:
nan
inf
所以在 NaN 恶作剧的情况下,我们会根据参数的顺序得到不同的答案。
affinely-extended 实数(即包括 +/-inf 但不包括 NaN)上的 min
显然是一个幺半群。
然而,将任何东西与 NaN
进行比较的结果是 而不是 false,而是“unordered”。这意味着 <
只是 float
上的偏序,因此 std::min<float>
(根据 <
定义)不是幺半群。
在 IEEE 754 中有一个 totalOrder
谓词 - 虽然我不知道它是如何在 C++ 中公开的,如果有的话。我们可以用这个代替 <
来写我们自己的 min
,并且 会 形成一个幺半群……但它不会是 std::min
.
为了确认,我们可以在 godbolt 上编译您的代码的一个变体,看看它在实践中是如何实现的:
与
comiss
进行比较,有可能的结果UNORDERED: ZF,PF,CF←111; GREATER_THAN: ZF,PF,CF←000; LESS_THAN: ZF,PF,CF←001; EQUAL: ZF,PF,CF←100;
并指定
The unordered result is returned if either source operand is a NaN (QNaN or SNaN).
分支已通过
jbe
完成,这将Jump short if below or equal (CF=1 or ZF=1).
你可以看到 UNORDERED 结果实际上被这个条件分支视为小于 和 相等。
所以,假设这是 IEEE 754 提到的无序比较的合法模型,应该允许两者都存在
min(min(+inf, NaN), -inf) =
min(+inf, -inf) = -inf
和
min(+inf, min(NaN, -inf)) =
min(+inf, NaN) = +inf
这意味着 min<float>
不是关联的,因此不是幺半群。