9

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

如果一个类将一个成员初始化valstd::numeric_limits<double>::infinity()然后想要检查 val 是否已更改为有效的值(+/- inf 在这里无效),这三种方法的权衡是什么,我是否错过了任何其他有趣的方法来解决这个问题. (为了便于阅读,在示例中删除了 const。)

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. 或者如果 +/-inf 在未来变得有效,那么安静的 NaN 会是一个更安全的选择吗?

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

为了方便参考:

排序相关:

4

2 回答 2

4

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

就这三种方法而言,只有方法“a”与初始值完全匹配。因此,如果不变量是只有那个初始值代表初始化值,这是最准确的方法。当然,这些方法都没有对保护不变量一开始做任何事情,这在我看来是一个更大的问题:不变量是否或可以有效地执行。

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

  • - 基于其IsInit代码 - 似乎意味着返回 AABB 是否已由用户实际设置。不是它是否像可以说的IsInit那样处于初始状态/价值(并误导我相信)。我个人更喜欢IsValid这个测试的名字。
  • 至于不变量实际上是什么,编辑本身似乎认识到一个有效的 AABB 永远不应该有一个MinX大于MaxXMinY大于的值MaxY。换句话说,它们不应该在有效的 AABB 中反转。MinX应始终小于或等于MaxX(最小和最大 Y 变量相同)。
  • 使用反向无穷大值,使其:(a)更容易潜在地增长 AABB 以包围其他 AABB(并且编辑演示了我将代码行从 14 行减少到 4 行的意思);(b) 使其能够处理比使用max()andlowest()值更广泛的有效 AABB。
  • MinX, MaxX, MinY,成员变量是可公开访问的这一事实MaxY意味着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 的最大值。

于 2017-03-21T15:10:04.003 回答
0

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

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

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

于 2019-05-03T19:55:42.763 回答