我想在ios4中实现正弦和余弦计算器:
if([operation isEqual:@"sin"]){
operand = (operand*M_PI/180.0);
operand=sin(operand);
}
代码为我提供了从0到90的正确答案。
当我给出180的值时,我得到1.22465e-16
作为答案。我期待零。
这个小小的差异来自哪里?
答案 0 :(得分:7)
为了清楚起见,您的程序 为您提供正确答案。也就是说,它完全按照你在代码中告诉它做的那样。
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);
请同时提交一个错误请求sinpi
作为扩展名添加到数学库。
答案 1 :(得分:5)
这是由于二进制数系统无法准确表示PI。
一种可能的解决方案是使用sin的对称性:
sindeg(x) = sindeg(180 - x)
(或者):
sin(x) = sin(M_PI - 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);
}
答案 2 :(得分:3)
您可能想要检查此平台上的数学库是否提供函数sinpi()作为C / C ++标准数学库函数的扩展。这样可以避免与pi的近似值(即M_PI)显式相乘,并提高精度。
operand = sinpi (operand / 180.0)
答案 3 :(得分:-1)
您依赖处理器中的浮点数据类型。通过(非常可观)更多的努力,您可以实现任意精度数据类型。看看MPFR http://www.mpfr.org/sample.html它包含的函数定义(如sin)将提供更多的数字。