【问题标题】:Why do sin(45) and cos(45) give different results? [duplicate]为什么 sin(45) 和 cos(45) 给出不同的结果? [复制]
【发布时间】:2015-10-09 03:13:25
【问题描述】:

这是我没想到的。我知道这些数字不是 100% 准确的,但我没想到互补角度会给出 sincos 的不同结果:

以下函数返回0.70710678118654746000000...

sin(45 * PI / 180.0);

虽然下面的函数返回0.70710678118654757000000...

cos(45 * PI / 180.0);

所以,它是:

0.707106781186547**46**000000... vs
0.707106781186547**57**000000...

不仅如此……sin(1 * PI / 180.0) 还返回与cos(89 * PI / 180.0) 略有不同的数字,尽管它们应该相同。

此外,这不仅是 sincos 的问题,也是 sinsin 的问题:sin(1 * PI / 180.0) 返回的值与 sin(179 * PI / 180.0) 不同,它们应该是相同的。

我尝试使用弧度而不是度数,但差异完全相同,我尝试使用较小的 PI 值,较大的 PI 值(大约 100 位小数或更多),但它们仍然不同,我尝试使用cmath而不是math.h,我尝试使用M_PI而不是我自己定义的PI

差异始终相同,大约在小数点后 16 位左右。不要误会我的意思,我知道我永远不会得到这些数字的 100% 精确值,但至少我期望得到与互补角 sincos 相同的“不精确”值。这到底是怎么回事?


我需要它们相同,因为我正在处理的程序(我被要求做的重力模拟器)使用具有double(我也尝试过float)变量的对象,这些变量基本上是角度(度或弧度,我都试过了)。这些是物体用来移动的方向,我也需要角度来计算物体之间的相互作用。

在程序的每次迭代中角度都会发生变化,并且在每次迭代中,角度都会根据上一次迭代角度的计算而变化,因此,如果在任何点有任何最小错误的角度值,那么该错误就会被放大更多并且每次迭代都有更多。

程序运行了数千甚至数百万次迭代,因此值的错误变得非常大!说清楚,行星最终会失去平衡,一切都变成了一场灾难,我真的很生气:(

附:我在 Windows 7 上,32 位。

【问题讨论】:

  • 双精度只有大约 16 位小数。
  • 好吧,我知道我永远不会得到 sin(45) 的完美值,这样的值不存在,完美的圆形 PI 值也不存在,但我在最不希望互补角以同样的方式出错:/
  • 您可能想要寻找(或实现)一个符号计算库。当然,您会牺牲性能来换取精度。

标签: c++ floating-point trigonometry angle pi


【解决方案1】:

我知道我永远不会得到这些数字的 100% 精确值,但至少我期望得到互补角的 sin 和 cos 的相同“不精确”值。

为什么?的计算方式不同,因此会发生(并累积)不同的浮点错误。你看到的不是错误; FP 算术无法通过数学定律预测。

顺便说一句,提供例如。 30 位或 100 位 PI 不会有什么不同
如果您的类型一开始就不能容纳 30 位数字。

【讨论】:

  • 我知道关于 PI 的事情,这是一次让它正常工作的绝望尝试。那么,这显然是一个巨大的错误,无论它们是如何计算的,它们都应该返回相同的值:/ 感谢您的回答!
  • @Dimakhaerus 如果您认为这是一个错误,那么提出解决方案怎么样?是的,不可能实现这样的期望,因为这样的特殊情况是无限的。例如sin(60)==sqrt(3)/2 是另一个。 ...如果没有无限量的内存,就会出现错误,没有什么可以阻止。
  • 对于大多数用途而言,即使提供 30 位数字也完全是多余的,355/113 根据记忆,足以在地球表面定位汽车。 3.141592653589(我脑海中浮现的)可能(虽然我还没有检查过)在太阳系中找到一颗豌豆:-)
  • @deviantfan 好吧,我不知道解决方案,我对这个问题以及如何解决它一无所知:/ 我唯一能想到的就是创建一个等于值的函数所有这些数字应该相同,例如:当程序需要计算 "cos(270°)" 或 "cos(90°)" 或 "sin (180°)" 时,请进行其他计算: “sin(0°)”,它们都将返回相同的值(因为它们都会进行相同的实际计算(sin 0°)......也许这就是 sin 和 cos 函数的工作方式,但我很好不确定,我显然远非专家。
  • @Dimakhaerus 你首先想实现什么?为了比较浮点数,“安全边际”通常是一个好主意,即。如果它们之间的差异小于 0.00001 或类似的值,您将它们视为平等。
【解决方案2】:

无论它们是如何计算的,它们都应该返回相同的值

您的期望不正确。在 IEEE-754 中,只有基本运算符 (+-*/) 和 sqrt 需要正确舍入。 sincosexp...等超越函数不是必需的,因为它非常复杂

没有标准要求对超越函数进行忠实四舍五入。 IEEE-754 (2008) 建议,但不要求正确舍入这些函数。


现在如果你看看你的价值观

                                      ↓
0.70710678118654746 = 0x1.6a09e667f3bccp-1
0.70710678118654757 = 0x1.6a09e667f3bcdp-1
                                      ↑

所以它们之间的距离在 1ulp 以内,并且在双精度方面足够精确

不要误会我的意思,我知道我永远不会得到这些数字的 100% 精确值,但至少我期望得到与互补角的 sin 和 cos 相同的“不精确”值

计算sincos 的算法不止一种。对于某些输入集,每个输入都是正确的,但对于其他一些输入则不正确。它们还具有不同的内存和时间要求,因此有些可能非常快但误差较高,有些则需要更多时间和内存,但它们可以实现更高的准确度。

编译器实现可能对这些函数使用不同的算法,因此如果您需要一致的结果,请使用一个精心设计的跨平台库。例如,GCC 使用MPFR 来实现正确舍入的结果,而不管平台如何

GCC 中端已与 MPFR 库集成。这允许 GCC 在编译时评估和替换对具有常量参数的内置数学函数的调用及其数学等效结果。在使用 MPFR 时,无论主机平台的数学库实现或浮点精度如何,GCC 都可以生成正确的结果。这也允许 GCC 生成相同的结果,无论是在本机或交叉编译配置中编译到特定目标

https://gcc.gnu.org/gcc-4.3/changes.html#mpfropts

How does C compute sin() and other math functions?

【讨论】:

  • 谢谢!它让我更好地了解程序失败的原因,当我的 cos 和 sin 值应该相同时,我会尝试强制它们实际上相同,如果这不起作用,我会尝试和你说的不同的图书馆! :)
  • 只是一个小附录:即使你有sincos 的“精确”实现(即,误差永远不会超过0.5ulp),你也不会得到相同的结果这里的正弦和余弦值,因为输入参数不完全是 PI/4(取决于这个输入值的四舍五入方式,正弦或余弦会更大)。在计算 sin(M_PI)(不是 0.0)时,您实际上可以观察到类似的问题
猜你喜欢
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 2020-11-23
  • 2015-10-08
  • 1970-01-01
  • 2017-04-29
  • 2021-12-07
  • 2020-10-15
相关资源
最近更新 更多