【问题标题】:Best way to get cos(a) from sin(a)从 sin(a) 得到 cos(a) 的最佳方法
【发布时间】:2011-11-23 18:38:06
【问题描述】:

s 作为某个(未知)角“a”的正弦,获得“a 的余弦”的最快方法是什么?

我知道两种显而易见的方式:

c = cos(asin(s));

c = sqrt(1 - s*s);

但是我不知道函数 cos()、asin() 和 sqrt() 的实现在速度方面如何相互比较。一个比另一个快多少?它们在现代处理器中的实现之间是否存在显着差异,例如,在 x86-64 和带有 VFP 的 ARM 之间?最后,有什么更好的解决方案?

编辑:由于现在已经有 3 个不相关的答案,让我澄清一下:我最初没有角度,我只有正弦。所以没有必要告诉我将角度旋转 90 度,这样我就可以从其他函数中获得相同的值...

【问题讨论】:

  • 你熟悉Premature Optimization这个概念吗?
  • 测量一下你就知道了。但要注意溢出。
  • 将此问题的范围缩小到特定的编程语言可能会有所帮助。
  • 您的精度要求是什么?你可以近似你的函数。

标签: c++ floating-point trigonometry


【解决方案1】:

这是一种方法:

sin(a)^2 + cos(a)^2 = 1(毕达哥拉斯)

cos(a) = sqrt(1 - sin(a)^2))

您需要找出不同的象限(即分别计算 cos() 的符号)。如果你只有 sin() 值,这是不可能的(不同的角度可以有相同的 sin() 但 cos() 的符号不同)。

正如其他人指出的那样,查找表实际上可能是最快的。取决于你需要什么精度。在实践中,这几乎肯定会比您的 cos(asin()) 版本和 square root can also be optimized 更快。

  • SSE 和 ARM-NEON 具有平方根指令,但没有三角函数。
  • x87 具有平方根和 sin/cos,但没有反 sin/cos 函数。平方根比 sin/cos 快。

使用 Visual Studio 2010,这种方法的性能比我的 Core i3 笔记本电脑上基于 trig 的版本(带有快速浮点选项)快大约 6 倍(每次调用大约 20ns)。我们看一下生成的代码:

快速浮点选项,使用平方根:

; 15   :     return sqrt(1.0 - s*s);

    movsd   xmm1, QWORD PTR __real@3ff0000000000000
    mulsd   xmm0, xmm0
    subsd   xmm1, xmm0
    sqrtsd  xmm0, xmm1

使用三角函数:

; 22   :     return cos(asin(s));
call    ___libm_sse2_asin
jmp ___libm_sse2_cos

当切换到精确浮点模式时,生成的三角代码使用不同的函数(可能是 SSE 优化版本牺牲了准确性):

fld QWORD PTR _angle_sin$[esp+esi+65600]
call    __CIasin
call    __CIcos
fstp    QWORD PTR _angle_cos$[esp+esi+65600]

【讨论】:

    【解决方案2】:

    最好的方法是使用eps * sqrt(1 - s * s),其中eps 是正一或负一。这是最好的方法

    • 在准确度方面,因为1 - s * s 保持接近一,因此计算平方根的误差很小。如果你在计算反正弦的余弦可能会失去精度'near one' 案例(练习:1/ 为什么?提示:记住 sin 在 pi/2 处最大,它的导数消失 2/ 试试看。)编辑:我说得太快了,因为@当s 接近一时,987654324@ 也会出现同样的问题。
    • 在速度方面:平方根很容易(一些类似牛顿的方法的一些算术运算),但不清楚asin 做了什么(可能相当昂贵),cos 可能是一个订单比sqrt 慢的数量级,因此一个平方根可能比这两个超越函数调用更快。有疑问,个人资料(但我准备赌点钱)。

    在您证明sqrt(1 - s * s) 对您来说不够快之前,请忘记查找表(即使在那里,您也可以找到一些方法来权衡sqrt 的准确性以换取速度)。

    【讨论】:

    • 保持接近 1 是什么意思? 1-sin^2 将介于 1 和 0 amd 之间,平方根也是如此......
    • @Guy:实际上我说得太快了:如果 s 接近 1,那么 1 - s * s 接近于零,并且平方根变得不精确(导数在零处发散)。所以它似乎在这方面遇到了与 cos(asin) 方法相同的问题。但是,它仍然可能更快。
    • 我明白了,但我说的是该段的第一部分:“因为 1 - s * s 保持接近 1,因此计算平方根的误差很小。”。 (1 - s*s) 从 0 到 1 会更好地表达这个 IMO,我不确定当 s 接近 1 时的准确性,尽管我没有深入考虑过。
    • 是的(稍后编辑)...我同意它会更快,因此我的回答:-)
    • 好吧,也许我应该在问之前测试它,但是既然我问了,每个人都告诉我应该剖析,好吧,我已经剖析了。使用 sqrt 比使用 asin+cos 近 30%(至少在我的 Linux x86-32 机器上,可能在其他架构中有所不同)。我很怀疑,因为很久以前我在一个图形应用程序中使用 sqrt,它遇到了严重的性能问题。当我用三角函数代替它时,问题就消失了。今天我不得不再次在它们之间做出选择,我很想知道从那以后事情发生了怎样的变化。
    【解决方案3】:

    用极坐标可视化单位圆。 r=1,theta =(角度)。那么单位圆上 X,Y(笛卡尔)坐标中的任意一点都是(cos(theta),sin(theta))。

    【讨论】:

      【解决方案4】:

      Sin 和 Cos 只是同一条曲线偏移二分之一弧度(或 90 度),所以:

      sin(a) = cos(a + pi/2)
      cos(a) = sin(pi/2 - a)
      

      pi 是 3.14159...

      【讨论】:

      • 但是我没有a,我只有s,这是某个角度的正弦。
      • 啊,好吧,那么你的 cos(asin(s)) 可能是最快的。但无论如何都要进行基准测试。主要的数学库通常具有硬编码的特殊条件,因此如果您所做的只是 45/90/135 类型角度,那么这些可能比您执行任意 1.37、87.3 等...类型角度要快得多。跨度>
      【解决方案5】:

      如果你的 sin/cos 函数采用弧度:

      cos(x) === sin(x+pi/2)
      

      如果你的 sin/cos 函数取度数:

      cos(x) === sin(x+90)
      

      【讨论】:

      • 我认为 OP 在问:鉴于 sin(x) 的值,如何计算 cos(x),而不是如何将 cos 减少到 sin
      • @larsman:给定任一值,将其反转并执行 +/- pi/2 并且 OP 得到了他们想要的答案。
      • @MarcB,不,我需要 x,我没有。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多