c++ - 内联函数展开 - 内联函数



函数的内联版本返回的值不同于非内联版本 (2)

相同功能的两个版本如何只有一个是内联而另一个不同,返回不同的值? 这是我今天写的一些代码,我不确定它是如何工作的。

#include <cmath>
#include <iostream>

bool is_cube(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

bool inline is_cube_inline(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

int main()
{
    std::cout << (floor(cbrt(27.0)) == cbrt(27.0)) << std::endl;
    std::cout << (is_cube(27.0)) << std::endl;
    std::cout << (is_cube_inline(27.0)) << std::endl;
}

我希望所有输出都等于 1 ,但它实际输出(g ++ 8.3.1,没有标志):

1
0
1

代替

1
1
1

编辑:clang ++ 7.0.0输出:

0
0
0

和g ++ - 这个:

1
1
1

Answer #1

说明

在编译时评估表达式时,某些编译器(尤其是GCC)使用更高的精度。 如果表达式仅依赖于常量输入和文字,则可以在编译时对其进行求值,即使表达式未分配给constexpr变量也是如此。 是否发生这种情况取决于:

  • 表达的复杂性
  • 编译器在尝试执行编译时评估时用作截止值的阈值
  • 在特殊情况下使用的其他启发式方法(例如clang elides循环时)

如果显式地提供了表达式,如第一种情况那样,它具有较低的复杂性,并且编译器可能在编译时对其进行评估。

类似地,如果函数被内联标记,则编译器更有可能在编译时对其进行求值,因为内联函数会提高评估可能发生的阈值。

更高的优化级别也会增加此阈值,如在-Ofast示例中,由于更高精度的编译时评估,所有表达式在gcc上的计算结果为true。

我们可以在编译器资源管理器上观察到这种行为。 使用-O1编译时,仅在编译时评估标记为内联的函数,但在-O3时,两个函数在编译时进行评估。

注意:在编译器 - 资源管理器示例中,我使用 printf 而不是iostream,因为它降低了主函数的复杂性,使效果更加明显。

证明 inline 不会影响运行时评估

我们可以通过从标准输入获取值来确保在编译时不会评估任何表达式,当我们这样做时,所有3个表达式都返回false,如下所示: https://ideone.com/QZbv6Xhttps://ideone.com/QZbv6X

#include <cmath>
#include <iostream>

bool is_cube(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}
 
bool inline is_cube_inline(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

int main()
{
    double value;
    std::cin >> value;
    std::cout << (floor(cbrt(value)) == cbrt(value)) << std::endl; // false
    std::cout << (is_cube(value)) << std::endl; // false
    std::cout << (is_cube_inline(value)) << std::endl; // false
}

此示例相比 ,我们使用相同的编译器设置但在编译时提供值,从而实现更高精度的编译时评估。


Answer #2

如所观察到的,使用 == 运算符来比较浮点值导致具有不同编译器和不同优化级别的不同输出。

比较浮点值的一个好方法是文章中概述的 相对容差 测试: 重新考虑浮点容差

我们首先计算 Epsilon 相对容差 )值,在这种情况下:

double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();

然后以这种方式在内联函数和非内联函数中使用它:

return (std::fabs(std::floor(std::cbrt(r)) - std::cbrt(r)) < Epsilon);

现在的功能是:

bool is_cube(double r)
{
    double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();    
    return (std::fabs(std::floor(std::cbrt(r)) - std::cbrt(r)) < Epsilon);
}

bool inline is_cube_inline(double r)
{
    double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();
    return (std::fabs(std::round(std::cbrt(r)) - std::cbrt(r)) < Epsilon);
}

现在输出将按预期( [1 1 1] )与不同的编译器和不同的优化级别。

现场演示





c++