Ios4 正弦180的值为1.22465e-16

Ios4 正弦180的值为1.22465e-16,ios4,floating-point,Ios4,Floating Point,我想在ios4中实现一个正弦和余弦计算器: if([operation isEqual:@"sin"]){ operand = (operand*M_PI/180.0); operand=sin(operand); } 代码为0到90之间的值提供了正确答案 当我给出180的值时,我得到1.22465e-16作为答案。我期望零 这个微小的差别是从哪里来的?这是由于二进制数字系统不能准确地表示π造成的 一种可能的解决方案是使用sin的对称性: sindeg(x) = sindeg(

我想在ios4中实现一个正弦和余弦计算器:

if([operation isEqual:@"sin"]){
    operand = (operand*M_PI/180.0);
    operand=sin(operand);
}
代码为0到90之间的值提供了正确答案

当我给出180的值时,我得到
1.22465e-16
作为答案。我期望零


这个微小的差别是从哪里来的?

这是由于二进制数字系统不能准确地表示π造成的

一种可能的解决方案是使用sin的对称性:

sindeg(x) = sindeg(180 - x)
(或选择):

将角度转换为范围(-pi/2:pi/2)可减少近似误差

基本上:

if([operation isEqual:@"sin"]){
  operand = fmod(operand, 360);
  if (operand > 270) operand -= 360;
  else if (operand > 90) operand = 180 - operand;
  operand=sin(operand*M_PI/180.0);
}

您依赖于处理器中的浮点数据类型。通过(相当大的)努力,您可以实现任意精度的数据类型。看看MPFR,它包含的函数定义(如sin)将提供更高的位数。

您可能需要检查此平台上的数学库是否提供函数sinpi(),作为C/C++标准数学库函数的扩展。这将避免与pi的近似值(即M_pi)的显式相乘,并提高精度

operand = sinpi (operand / 180.0)

为了清楚起见,您的程序正在为您提供正确答案。也就是说,它完全按照您在代码中告诉它的做

180*M_PI
正确四舍五入(根据IEEE-754),并给出以下值:

565.4866776461627750904881395399570465087890625
将其除以180也可正确四舍五入,并得出结果:

3.141592653589793115997963468544185161590576171875
这不完全是π的数学值。事实上,它是:

π - 0.0000000000000001224646799147...
π附近
sin(x)
的泰勒级数的一阶项是(π-x),因此
sin(π-x)
对于小
x
,几乎完全是
-x
。事实上,您得到的结果是正确的四舍五入结果。图书馆不可能给出更准确的答案

正如Ben Voigt所建议的,如果这对您来说确实是个问题,您可以通过将参数减少到范围内来解决它[-90,90),然后再从度转换为弧度。一个更好的建议是njuffa,使用
sinpi
函数来为您完成这项工作。iOS没有这样的函数,但它有
vvsinpi
,它为向量实现sin(π*x),并且可以让它执行您想要的操作:

double result;
int vectorLength = 1;
vvsinpi(&result, &operand, &vectorLength);

还请提交一个bug,请求将
sinpi
作为扩展添加到数学库中。

我不是反对者,但我猜这是因为您的第二个表达式,它实际上不如典型的数学库实现准确(正是因为pi不可表示)@Stephen:小x的精确度较低,但接近π的x的精确度更高(如果
x
是按照
M_π
计算的,问题是这样的)。
\u sinpi()
从iOS 7和OS x 10.9开始提供。如果需要,还可以使用_cospi(val/180),\u tanpi(val/180)
double result;
int vectorLength = 1;
vvsinpi(&result, &operand, &vectorLength);