【问题标题】:c++ libstd compute sin and cos simultaneouslyc++ libstd同时计算sin和cos
【发布时间】:2014-06-20 13:14:47
【问题描述】:

在 C 库 math.h 中,有一个非常高效的 sincos 函数,因为它计算正弦和余弦的时间更接近于对 sin()cos() 的一次调用,而不是总时间调用两者。

C++标准库中有这样的功能吗?

【问题讨论】:

  • 这里有几点建议:stackoverflow.com/questions/2683588/…
  • 其实我已经有了自己的 sincos SSE 优化函数,但是在这里我正在编写一个示例插件,我希望使用标准库以外的任何其他库来使其尽可能简单
  • GCC 处于低优化水平 (-O1),通常-Ofast 会为您执行此操作。 godbolt.org/z/jCiTDo
  • 我会分析 std::exp(I * angle),或者查看它生成的代码 - 希望是(可能是内部的)优化的 sincos 函数。无论如何,它不会比分别调用sincos 更糟糕,并且应该避免在尝试通过三角身份从另一个推导时出现数值不稳定性。

标签: c++ math libstdc++ trigonometry


【解决方案1】:

c++标准库中没有这个函数吗?

不,很遗憾没有。

在 C 库 math.h 中,有一个 sincos 函数

在 Linux 上,它以 GNU Extension 的形式提供。这在 C 中也不是标准的。

【讨论】:

    【解决方案2】:

    只需分别使用 sin 和 cos 并打开优化。 C 编译器非常擅长优化,他们可能会意识到您正在计算同一个变量的正弦和余弦。如果你想确定,你总是可以检查生成的程序集(对于 gcc,使用 -S 选项)并查看它生成了什么。

    编译器可能会优化掉对sincos 的任何调用,以支持简单地使用SSE 指令来计算它。我不确定 SSE 是否有 sincos 操作码,但即使单独计算它们也比调用编译器不会优化的任何 sincos 函数更快。

    【讨论】:

    • SSE 不支持 sin/cos 或除除法和平方根之外的任何“花哨”函数。但是一些编译器(例如英特尔编译器)将具有使用 SSE 的矢量化实现。
    • SSE 没有 sin、cos 或 sincos 指令。只有基础算术。
    • 使用godbolt我确认了高优化级别的较新GCC版本确实可以将sin + cos合并为sincos,但是clang和VS2017没有这样的优化。
    • @DanOlson,我认为 MSVC 2017 及更高版本确实优化了正弦和余弦调用:devblogs.microsoft.com/cppblog/…
    • @DanOlson,在这里确认godbolt.org/z/wuS26Z 用于 GCC 和 intel 的优化水平相当低。 clang 仅在 -Ofast (快速数学)中进行优化。这提示我sincos 可能无法保证与sincos 分别调用的结果完全相同
    【解决方案3】:

    虽然没有标准的 C++ 库函数,但您可以很快地定义一个模板函数:

    template <class S>
    std::pair<S,S> sincos(S arg) { return { std::sin(arg), std::cos(arg) }; }
    

    然后,您可以在一行(使用 C++ 17)上获得结果:

    auto [s, c] = sincos(arg);
    

    如果你经常这样做,它会非常方便,节省空间,并且是自记录的,所以我强烈推荐它。 如果您担心性能,请不要。当使用优化编译时,它应该产生与分别调用 sin 和 cos 完全相同的代码。您可以在以下测试代码中使用clang++ -std=c++17 -S -o - -c -O3 sincos.cpp 确认这种情况:

    #include <cmath>
    #include <utility>
    #include <iostream>
    
    template <class S>
    std::pair<S,S> sincos(S arg) { return { std::sin(arg), std::cos(arg) }; }
    
    void testPair(double a) {
        auto [s,c] = sincos(a);
        std::cout << s << ", " << c << '\n';
    }
    
    void testSeparate(double a) {
        double s = std::sin(a);
        double c = std::cos(a);
        std::cout << s << ", " << c << '\n';
    }
    

    在带有 clang 的 MacOS 上,两个测试函数都编译为完全相同的程序集(减去名称更改),调用 ___sincos_stret 来执行组合计算(请参阅 https://stackoverflow.com/a/19017286/973580)。

    【讨论】:

      【解决方案4】:

      相反,你可以使用这个只使用 std::cos 和 std::sqrt 的函数(还没有实际测试过,它可能不起作用)

      template <typename T>
      constexpr inline static void sincos(const T &x, T* sin, T* cos) {
          (*cos) = std::cos(x);
          (*sin) = std::sqrt(static_cast<T>(1) - *cos**cos);
          if ((int)(x / M_PI) & 1) (*sin) = -(*sin);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-10
        • 1970-01-01
        • 1970-01-01
        • 2011-02-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多