【问题标题】:Most efficient way to determine if value between two other values, inclusive确定值是否介于其他两个值之间的最有效方法,包括
【发布时间】:2019-06-04 16:50:39
【问题描述】:

类似于Fastest way to determine if an integer is between two integers (inclusive) with known sets of values,我想知道某个值(很可能是双精度浮点数)是否介于其他两个值(相同类型)之间。需要注意的是,我还不知道哪个值大于另一个值,我正在尝试确定是否应该/如何避免使用 std::max/min。这是我已经尝试过测试的一些代码:

inline bool inRangeMult(double p, double v1, double v2) {
    return (p - v1) * (p - v2) <= 0;
}

inline bool inRangeMinMax(double p, double v1, double v2) {
    return p <= std::max(v1, v2) && p >= std::min(v1, v2);
}

inline bool inRangeComp(double p, double v1, double v2) {
    return p < v1 != p < v2;
}

int main()
{
    double a = 1e4;

    std::clock_t start;
    double duration;
    bool res = false;

    start = std::clock();
    for (size_t i = 0; i < 2e4; ++i) {
        for (size_t j = 0; j < 2e4; ++j) {
            res = inRangeMult(a, i, j) ? res : !res;
        }
    }
    duration = std::clock() - start;
    std::cout << "InRangeMult: " << duration << std::endl;

    start = std::clock();
    for (size_t i = 0; i < 2e4; ++i) {
        for (size_t j = 0; j < 2e4; ++j) {
            res = inRangeMinMax(a, i, j) ? res : !res;
        }
    }
    duration = std::clock() - start;
    std::cout << "InRangeMinMax: " << duration << std::endl;

    start = std::clock();
    for (size_t i = 0; i < 2e4; ++i) {
        for (size_t j = 0; j < 2e4; ++j) {
            res = inRangeComp(a, i, j) ? res : !res;
        }
    }
    duration = std::clock() - start;
    std::cout << "InRangeComp: " << duration << std::endl;

    std::cout << "Tricking the compiler by printing inane res: " << res << std::endl;
}

在大多数运行中,我发现使用 std::min/max 仍然是最快的,(最新运行分别打印 346、310 和 324),但我不是 100% 确信这是最好的测试设置,或者我已经用尽了所有合理的实现方式。

如果有人提供更好的分析设置和/或更好的实施,我将不胜感激。

编辑:更新了代码,使其不易受到编译器优化的影响。

第二次编辑:调整了 a 的值和迭代次数。一次运行的结果是:

  • inRangeMult: 1337
  • inRangeMinMaz: 1127
  • inRangeComp: 729

【问题讨论】:

  • 要知道,如果您使用 inline 内联这些函数以获得性能提升,那么 inline 关键字在确定何时内联函数时不会有太大的权重(如果有的话)。
  • 你的测试很不平衡;结果几乎总是错误的。
  • 看起来很可能,因为a 本质上是constexpr 并且您从不使用函数的结果,因此您的函数调用既可以在编译时预先计算,也可以完全优化。我不会指望这个基准的准确性。
  • 你是怎么编译的?我得到inRangeComp 是最快的。此外,您可以使用 this 之类的单个分支制作最小最大版本

标签: c++ performance math


【解决方案1】:

第一次测试:

(p - v1) * (p - v2) <= 0

由于算术运算,可能导致上溢或下溢。

最后一个:

p < v1 != p < v2

不提供与其他结果相同的结果,包括边界v1v2。考虑到double 类型的范围和精度,这无疑是一个很小的差异,但它可能很重要。

另一种选择是显式扩展第二个函数的逻辑:

p <= std::max(v1, v2) && p >= std::min(v1, v2)     // v1 and v2 are compared twice

变成这样:

bool inRangeComp(double p, double v1, double v2) {
    return v1 <= v2                                // <- v1 and v2 are compared only once
        ? v1 <= p && p <= v2 
        : v2 <= p && p <= v1;
}

至少有一个编译器 (gcc 8.2),HERE(感谢 jarod42 链接的 sn-p),似乎更喜欢这个版本而不是其他版本。

【讨论】:

  • 尼特,? v1 &lt;= p &amp;&amp; p &lt; = v2 : v2 &lt;= p &amp;&amp; p &lt;= v1; 可能更具可读性(取决于你,有些人更喜欢尤达条件)
  • 这一切都做同样的事情,但是当你变老时,以这种方式在其他两个值的中间阅读p 会更容易。密切关注潜在的溢出问题。
猜你喜欢
  • 1970-01-01
  • 2013-06-10
  • 2021-09-29
  • 1970-01-01
  • 2021-08-23
  • 2013-09-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多