Iphone 在计算角度时避免atan2-atan2精度
我们,开发人员经常需要计算角度来执行旋转。通常我们可以使用atan2()函数,但有时我们需要更高的精度。那你怎么办Iphone 在计算角度时避免atan2-atan2精度,iphone,objective-c,c,math,geometry,Iphone,Objective C,C,Math,Geometry,我们,开发人员经常需要计算角度来执行旋转。通常我们可以使用atan2()函数,但有时我们需要更高的精度。那你怎么办 我知道理论上atan2是精确的,但在我的系统(iOS)中,它的精度约为0.05弧度,所以差别很大。这不仅仅是我的问题。我也看到过类似的观点。如果系统中的长双精度比双精度更精确,则可以使用atan2l long double atan2l(long double y, long double x); atan2用于从向量(x,y)获取角度a。如果使用此角度应用旋转,则将使用cos(
我知道理论上atan2是精确的,但在我的系统(iOS)中,它的精度约为0.05弧度,所以差别很大。这不仅仅是我的问题。我也看到过类似的观点。如果系统中的
长双精度
比双精度
更精确,则可以使用atan2l
long double atan2l(long double y, long double x);
atan2
用于从向量(x,y)
获取角度a
。如果使用此角度应用旋转,则将使用cos(a)
和sin(a)
。您可以通过归一化(x,y)简单地计算cos和sin,并保留它们而不是角度。精度将更高,并且可以节省在三角函数中丢失的大量周期
编辑。如果你真的想要一个从(x,y)开始的角度,可以使用的变量计算出你需要的精度。在iOS上,我发现标准的三角运算符精确到大约13或14位小数,所以你看到0.05弧度的误差听起来很奇怪。如果您可以生成代码和特定值来演示这一点,请在行为上进行说明(并在此处发布代码,以便我们可以对其进行记录) 也就是说,如果您真的需要三角运算符的高精度,我已经修改了Dave DeLong为其代码创建的一些例程。这些例程使用NSDecimal执行数学运算,为您提供多达34位的十进制精度,同时避免了以10位小数为基数表示的标准浮点问题。您可以从下载这些修改例程的代码 使用以下代码计算NSDecimal版本的
atan()
:
NSDecimal DDDecimalAtan(NSDecimal x) {
// from: http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Infinite_series
// The normal infinite series diverges if x > 1
NSDecimal one = DDDecimalOne();
NSDecimal absX = DDDecimalAbsoluteValue(x);
NSDecimal z = x;
if (NSDecimalCompare(&one, &absX) == NSOrderedAscending)
{
// y = x / (1 + sqrt(1+x^2))
// Atan(x) = 2*Atan(y)
// From: http://www.mathkb.com/Uwe/Forum.aspx/math/14680/faster-Taylor-s-series-of-Atan-x
NSDecimal interiorOfRoot;
NSDecimalMultiply(&interiorOfRoot, &x, &x, NSRoundBankers);
NSDecimalAdd(&interiorOfRoot, &one, &interiorOfRoot, NSRoundBankers);
NSDecimal denominator = DDDecimalSqrt(interiorOfRoot);
NSDecimalAdd(&denominator, &one, &denominator, NSRoundBankers);
NSDecimal y;
NSDecimalDivide(&y, &x, &denominator, NSRoundBankers);
NSDecimalMultiply(&interiorOfRoot, &y, &y, NSRoundBankers);
NSDecimalAdd(&interiorOfRoot, &one, &interiorOfRoot, NSRoundBankers);
denominator = DDDecimalSqrt(interiorOfRoot);
NSDecimalAdd(&denominator, &one, &denominator, NSRoundBankers);
NSDecimal y2;
NSDecimalDivide(&y2, &y, &denominator, NSRoundBankers);
// NSDecimal two = DDDecimalTwo();
NSDecimal four = DDDecimalFromInteger(4);
NSDecimal firstArctangent = DDDecimalAtan(y2);
NSDecimalMultiply(&z, &four, &firstArctangent, NSRoundBankers);
}
else
{
BOOL shouldSubtract = YES;
for (NSInteger n = 3; n < 150; n += 2) {
NSDecimal numerator;
if (NSDecimalPower(&numerator, &x, n, NSRoundBankers) == NSCalculationUnderflow)
{
numerator = DDDecimalZero();
n = 150;
}
NSDecimal denominator = DDDecimalFromInteger(n);
NSDecimal term;
if (NSDecimalDivide(&term, &numerator, &denominator, NSRoundBankers) == NSCalculationUnderflow)
{
term = DDDecimalZero();
n = 150;
}
if (shouldSubtract) {
NSDecimalSubtract(&z, &z, &term, NSRoundBankers);
} else {
NSDecimalAdd(&z, &z, &term, NSRoundBankers);
}
shouldSubtract = !shouldSubtract;
}
}
return z;
}
nsdecimaldddecimalatan(nsdecimalx){
//发件人:http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Infinite_series
//如果x>1,则正规无穷级数发散
nsdecimalone=DDDecimalOne();
nsdecimalabsx=DDDecimalAbsoluteValue(x);
nsz=x;
if(NSDecimalCompare(&one,&absX)=取消搜索)
{
//y=x/(1+sqrt(1+x^2))
//Atan(x)=2*Atan(y)
//发件人:http://www.mathkb.com/Uwe/Forum.aspx/math/14680/faster-Taylor-s-series-of-Atan-x
根的内部;
NSDecimalMultiply(&interioroftroot,&x,&x,NSRoundBankers);
NSDecimalAdd(根的内部和一根的内部和根的内部,NSRONDBANKERS);
NSDecimal分母=DDDecimalSqrt(根的内部);
NSDecimalAdd(分母和一个分母和一个分母);
NSY;
NSDecimalDivide(&y,&x,&Deminator,NSR);
NSDecimalMultiply(&interioroftroot,&y,&y,NSRoundBankers);
NSDecimalAdd(根的内部和一根的内部和根的内部,NSRONDBANKERS);
分母=DDDecimalSqrt(根的内部);
NSDecimalAdd(分母和一个分母和一个分母);
NSY2;
NSDecimalDivide(&y2,&y,&Deminator,NSR);
//nsdecimaltwo=DDDecimalTwo();
NSDecimal四=DDDECIMAL整数(4);
NSDecimal第一反正切=DDDecimalAtan(y2);
NSDecimalMultiply(&z,&four,&firstArctangent,nsr);
}
其他的
{
BOOL shouldSubtract=是;
对于(n积分n=3;n<150;n+=2){
十进制分子;
if(NSDecimalPower(&分子,&x,n,NSRoundBankers)==NSCalculationUnderflow)
{
分子=DDDecimalZero();
n=150;
}
NSDecimal分母=DDDecimalFromInteger(n);
小数项;
if(NSDecimalDivide(&术语、分子和分母,NSRoundBankers)==NSCalculationUnderflow)
{
术语=小数点零();
n=150;
}
如果(应该减去){
NSDecimalSubtract(&z,&z,&term,nsr);
}否则{
NSDecimalAdd(&z,&z,&term,NSRONDMALADD);
}
shouldSubtract=!shouldSubtract;
}
}
返回z;
}
这使用了泰勒级数近似,带有一些加快收敛速度的快捷方式。我相信在非常接近Pi/4弧度的结果中,精度可能不是完整的34位数字,所以我可能仍然需要修正这个问题
如果您需要极高的精度,这是一个选项,但同样地,您报告的不应该是双值的,所以这里有些奇怪。经常使用角度?不,你没有。在我见过的10次开发人员使用角度的例子中,有7次他应该使用线性代数来代替,并且避免使用任何三角函数 旋转最好使用矩阵,而不是角度。另见这个问题:
您需要比1E-15弧度更高的精度吗?真的吗?可能是重复的不,不是重复的。这是关于iOS和其他系统的atan2问题的一般性问题。在这里,我不是在要求解决上面列出的问题(因为我的解决方法不同),我只是为了了解。我认为其他开发人员有时也会遇到类似的困难,所以这是一个一般性的问题。如果你有一个重复的例子,
atan2
产生了错误的答案,在任何平台上,向平台供应商提交一个bug。总是。bugreport.apple.com在这种情况下。如果你打算(听起来很荒谬)声称atan2的错误为+/-0.05,你应该给出一些产生如此大错误的输入值的具体示例。在当前的ARM iOS设备上,long double
只是映射到double
,所以用这个你不会得到任何精度。你将在iOS模拟器中获得额外的精度,因为它在Mac上运行