GCC中不同优化级别的不同输出

Yam*_*ari 6 gcc g++ sfml c++17

我正在重写我上学期为大学开发的光线追踪器,但遇到了以下问题:当我在 Debug 中编译和运行我的代码时,输​​出符合预期

预期结果

但是当我启用更高的优化级别(例如“-O2”)时,结果会完全不同:

实际结果

我不确定为什么会发生这种情况。我追踪到球体相交代码

//#pragma GCC push_options
//#pragma GCC optimize("O0")

Intersection Sphere::intersect(const Ray& ray, const float previous) const
{
    const auto oc = ray.origin - center_;
    const auto lhv = -dot(ray.direction, oc);
    const auto discriminant = lhv * lhv - (oc.lensqr() - radius_ * radius_);

    if (discriminant < 0.0F)
    {
        return Intersection::failure();
    }
    float distance;
    const auto rhv = std::sqrt(discriminant);
    const auto r = std::minmax(lhv + rhv, lhv - rhv);
    if (r.first <= 0.0F)
    {
        if (r.second <= 0.0F)
        {
            return Intersection::failure();
        }
        distance = r.second;
    }
    else
    {
        distance = r.first;
    }

    const auto hit = ray.getPoint(distance);
    const auto normal = (hit - center_).normalize();

    if (0.0F <= distance && distance < previous - epsilon)
    {
        return {distance, ray, this, normal, hit};
    }
    return Intersection::failure();
}

//#pragma GCC pop_options
Run Code Online (Sandbox Code Playgroud)

如果我在发布模式下取消注释 pragma,我会再次得到预期的结果。也许我的代码中有一些未定义的行为会导致这种情况?

您也可以在此处查看,因为不容易实现最小的可重现示例。https://github.com/Yamahari/RayTracer/blob/master/rt/solid/Sphere.cpp

(您也可以克隆 repo 并使用 cmake 构建项目,您只需要 SFML 作为依赖项。

将 -DSFML_INCLUDE_DIR="include_dir" 和 -DSFML_LIB_DIR="lib_dir" 与使用所需编译器编译的 sfml 库一起使用)

wal*_*nut 4

std::minmax返回对其参数的一对引用。如果您使用纯右值作为参数来调用它,则该对中的引用将在完整表达式结束后悬空,这意味着r.first访问

if (r.first <= 0.0F)
Run Code Online (Sandbox Code Playgroud)

这里会有未定义的行为。

因此,将lhv + rhv和存储lhv - rhv在变量中并调用std::minmax它们或使用

std::minmax({lhv + rhv, lhv - rhv})
Run Code Online (Sandbox Code Playgroud)

选择std::initializer_list重载,它返回一对实际值。


正如 @Jarod42 在评论中指出的,您实际上不需要std::minmax这里。rhv是调用的结果std::sqrt,因此它始终是非负的,从而lhv + rhv始终是较大(或相等)的值。

  • 由于 `rhv` 是 `sqrt` 的结果,因此“正”,您可以直接执行 `const auto [min, max] = std::pair(lhv - rhv, lhv + rhv);` (2认同)