val != std::numeric_limits:<double>:infinity() 或 !isinf(val) 或 isfinite(val)

val != std::numeric_limits<double>::infinity() or !isinf(val) or isfinite(val)

本文关键字:val infinity isfinite isinf gt lt std numeric limits double      更新时间:2023-10-16

这可能是自行车脱落,但也许我错过了一些有趣的东西......

如果一个类初始化一个成员valstd::numeric_limits<double>::infinity(),后来想检查 val 是否已更改为有效的东西(+/- inf 在这里无效),这 3 种方法的权衡是什么,我是否错过了任何其他有趣的方法来解决这个问题。(为了提高示例中的可读性,删除了常量。

bool IsInit() { return MinX != std::numeric_limits<double>::infinity(); }  // a
bool IsInit() { return !std::isinf(MinX); }  // b
bool IsInit() { return std::isfinite(MinX); }  // c

目前代码是 C++03,但选项将如何随着 C++11、C++14 和 C++17 的变化而变化。 例如,对于 C++17,此代码可能只是std::optional<double> val。 或者安静的 NaN 会是一个更安全的赌注,以防 +/-inf 在未来生效?

当我阅读此代码的补丁时,出现了这种情况:

  • https://github.com/OSGeo/gdal/commit/30194c640f403008625ff0c8be7aca155ac7ebe1
  • https://trac.osgeo.org/gdal/browser/trunk/gdal/ogr/ogr_core.h?rev=37821#L76

为了便于参考:

  • std::numeric_limits::infinity()
  • 标准::伊辛夫
  • 标准::是有限的
  • 标准::numeric_limits::quiet_NaN
  • 标准::可选

有点相关:

  • 使用 std::numeric_limits::max() 作为特殊的"标志"是否安全?

使用这些特殊值作为初始化标志仅在变量无法到达特殊值时才有效。在数学运算方面,无穷大并不难获得,因为溢出也会得到它。所以这可能是有问题的。如果安静 NaN 可用,则使用安静 NaN 会更容易使用,因为不变的是用户无法将此浮点值设置为此值。

就这三种方法而言,只有方法"a"与初始值完全匹配。因此,如果不变是只有该初始值表示初始化值,则这是最准确的方法。当然,这些方法一开始就没有保护不变性,这对我来说更多的是问题:不变性是否有效或是否可以有效执行。

虽然这不是一个代码审查网站,但就 github 上对OGREnvelope类(似乎只是一个 AABB)的链接代码编辑而言,我认为应该注意几点:

  • IsInit- 基于其代码 - 似乎意味着返回AABB是否实际由用户设置。而不是它是否像IsInit所暗示的那样处于初始状态/值(并误导我相信)。就个人而言,我更喜欢像IsValid这样的名字来做这个测试。
  • 至于不变性实际上是什么,编辑本身似乎认识到有效的 AABB 不应该有大于MaxXMinX值,也不应该有大于MaxYMinY。换句话说,它们永远不应该在有效的 AABB 中被逆转。MinX应始终小于或等于MaxX(最小和最大 Y 变量也是如此)。
  • 使用
  • 反向无穷大值可以:(a)更容易潜在地增长AABB以包裹其他AABB(编辑演示了我的意思,将代码行从14行减少到4行);(b)使其能够处理比使用max()lowest()值更广泛的有效AABB。
  • MinXMaxXMinYMaxY成员变量是可公开访问的,这意味着IsInit和不变本身只是建议性的,因为没有封装来保护不变量。

在注意到这些事情的更广泛背景下,NaN 是不合适的。不变性从来没有那么MinX != std::numeric_limits<double>::infinity().IsInit(在该代码中实现)充其量是不完整的。在它被重命名为类似IsValid的上下文中,逻辑上更一致的实现是:

bool IsValid() const
{
return !std::isnan(MinX) && !std::isnan(MinY) && MinX <= MaxX && MinY <= MaxY;
}

通过此实现,有效的 AABB 是一个OGREnvelope,其值都是有效数字(没有一个是 NaN),并且其最小值必须分别小于或等于 X 和 Y 的最大值。

我不认为这是一个公认的答案,但希望它确实提供了一些好的信息。

我使用 Godbolt 工具对上述三个提议的实现进行了直接比较: https://godbolt.org/z/184Zb6

基本的观察结果是,在打开编译器优化的情况下,与std::numeric_limits<double>::infinity()相比比std::isfinite()更有效(更少的CPU指令和内存移动);也就是说,由于添加了逻辑否定操作,它只比!std::isinf()更有效。